/*
* Title: CloudSimSDN
* Description: SDN extension for CloudSim
* Licence: GPL - http://www.gnu.org/copyleft/gpl.html
*
* Copyright (c) 2015, The University of Melbourne, Australia
*/
package org.cloudbus.cloudsim.sdn;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import org.cloudbus.cloudsim.core.CloudSim;
import org.cloudbus.cloudsim.core.SimEntity;
import org.cloudbus.cloudsim.core.SimEvent;
/**
* This represents switches that maintain routing information.
* Note that all traffic estimation is calculated within NOS class, not in Switch class.
* Energy consumption of Switch is calculated in this class by utilization history.
*
*
* @author Jungmin Son
* @author Rodrigo N. Calheiros
* @since CloudSimSDN 1.0
*/
public class Switch extends SimEntity implements Node{
//private static long cont=0;
//private static long MULTI = 1;
private static double POWER_CONSUMPTION_IDLE = 66.7;
private static double POWER_CONSUMPTION_PER_ACTIVE_PORT = 1;
/* based on CARPO: Correlation-Aware Power Optimization in Data Center Networks by Xiaodong Wang et al. */
int bw;
long iops;
double previousTime;
int rank = -1;
int currentupports=0;
int currentdownports=0;
NetworkOperatingSystem nos;
Node[] upports;
Node[] downports;
ArrayList links = new ArrayList();
ForwardingRule forwardingTable;
RoutingTable routingTable;
Hashtable processingTable;
public Switch(String name, int bw, long iops, int upports, int downports, NetworkOperatingSystem nos) {
super(name);
this.bw = bw;
this.iops = iops;
this.previousTime = 0.0;
this.nos=nos;
if (upports>0) this.upports = new Node[upports];
this.downports = new Node[downports];
this.forwardingTable = new ForwardingRule();
this.processingTable = new Hashtable();
this.routingTable = new RoutingTable();
}
@Override
public void startEntity() {}
@Override
public void shutdownEntity() {}
@Override
public void processEvent(SimEvent ev) {
int tag = ev.getTag();
switch(tag){
//case Constants.SDN_INTERNAL_PACKAGE_PROCESS: internalPackageProcessing(); break;
//case Constants.SDN_PACKAGE: sendToBuffer((Package) ev.getData()); break;
default: System.out.println("Unknown event received by "+super.getName()+". Tag:"+ev.getTag());
}
}
public void addLink(Link l){
this.links.add(l);
}
/************************************************
* Calculate Utilization history
************************************************/
private List utilizationHistories = null;
private static double powerOffDuration = 0; //if switch was idle for 1 hours, it's turned off.
public class HistoryEntry {
public double startTime;
public int numActivePorts;
HistoryEntry(double t, int n) { startTime=t; numActivePorts=n;}
}
public List getUtilizationHisotry() {
return utilizationHistories;
}
public double getUtilizationEnergyConsumption() {
double total=0;
double lastTime=0;
int lastPort=0;
if(this.utilizationHistories == null)
return 0;
for(HistoryEntry h:this.utilizationHistories) {
double duration = h.startTime - lastTime;
double power = calculatePower(lastPort);
double energyConsumption = power * duration;
// Assume that the host is turned off when duration is long enough
if(duration > powerOffDuration && lastPort == 0)
energyConsumption = 0;
total += energyConsumption;
lastTime = h.startTime;
lastPort = h.numActivePorts;
}
return total/3600; // transform to Whatt*hour from What*seconds
}
public void updateNetworkUtilization() {
this.addUtilizationEntry();
}
public void addUtilizationEntryTermination(double finishTime) {
if(this.utilizationHistories != null)
this.utilizationHistories.add(new HistoryEntry(finishTime, 0));
}
private void addUtilizationEntry() {
double time = CloudSim.clock();
int totalActivePorts = getTotalActivePorts();
if(utilizationHistories == null)
utilizationHistories = new ArrayList();
else {
HistoryEntry hist = this.utilizationHistories.get(this.utilizationHistories.size()-1);
if(hist.numActivePorts == totalActivePorts) {
return;
}
}
this.utilizationHistories.add(new HistoryEntry(time, totalActivePorts));
}
private double calculatePower(int numActivePort) {
double power = POWER_CONSUMPTION_IDLE + POWER_CONSUMPTION_PER_ACTIVE_PORT * numActivePort;
return power;
}
private int getTotalActivePorts() {
int num = 0;
for(Link l:this.links) {
if(l.isActive())
num++;
}
return num;
}
/*
private void updateTime(double now) {
this.previousTime = now;
}
private void internalPackageProcessing() {
if(updatePackageProcessing()) {
sendInternalEvent();
}
else {
System.err.println(CloudSim.clock() + ": " + getName() +": Nothing changed! omg");
sendInternalEvent();
}
}
private void sendInternalEvent() {
CloudSim.cancelAll(getId(), new PredicateType(Constants.SDN_INTERNAL_PACKAGE_PROCESS));
if(processingTable.size() != 0) {
// More to process. Send event again
double delay = this.nextFinishTime();
Log.printLine(CloudSim.clock() + ": " + getName() + ".sendInternalEvent(): next finish time: "+ delay);
send(this.getId(), delay, Constants.SDN_INTERNAL_PACKAGE_PROCESS);
}
}
// Return if anything removed?
private boolean updatePackageProcessing() {
double currentTime = CloudSim.clock();
double timeSpent = CloudSim.round(currentTime - this.previousTime);
if(timeSpent <= 0 || processingTable.size() == 0)
return false; // Nothing changed
//update the amount of iops processed this round
long processedThisRound= Math.round(timeSpent * iops * MULTI / processingTable.size())+1;
//update processing table; remove finished packs
List toRemove = new ArrayList();
for (Package key: processingTable.keySet()) {
//DEBUG ONLY
if(key.payload.requestId == 309) {
System.out.println("ID:309 HERE:rem_len="+processingTable.get(key)+","+key.size);
}
long remainingLength = processingTable.get(key);
remainingLength-=processedThisRound;
if (remainingLength <= 0) {// finished: remove from the list
toRemove.add(key);
} else { //not finished:update table, check if it is the smaller to be processed
processingTable.put(key, remainingLength);
}
}
// Remove all packages that is done.
for (Package pkg:toRemove){
processingTable.remove(pkg);
this.processPackageFinish(pkg);
}
updateTime(currentTime);
System.err.println(CloudSim.clock()+": Switch.updatePackageProcessing("+getName()+ ") #("+this.processingTable.size()+"):Time spent:"+timeSpent+
", Processed:"+processedThisRound);
if(toRemove.isEmpty())
return false; // Nothing changed
return true;
}
private void processPackageFinish(Package pkg) {
Log.printLine(CloudSim.clock() + ": " + getName() + ": finished processing a package:" + pkg);
nos.sendPackageToNextHop(this, pkg);
}
private void sendToBuffer(Package pkg) {
Log.printLine(CloudSim.clock() + ": " + getName() + ": Package received from Network:" + pkg);
updatePackageProcessing();
this.processingTable.put(pkg, pkg.getSize()*MULTI);
sendInternalEvent();
}
private double nextFinishTime() {
// Calculate the latest finish time among all jobs in the queue.
long smallerPkg = Long.MAX_VALUE;
for (Package key: processingTable.keySet()) {
// DEBUG
if(key.getPayload().requestId > 30 && key.getPayload().requestId < 35) {
System.out.println("Here!");
}
if(key.getPayload().requestId > 530 && key.getPayload().requestId < 535) {
System.out.println("Here!");
}
long remainingLength = processingTable.get(key);
if (remainingLength"+
NetworkOperatingSystem.debugVmIdName.get(dest) + ", flow ="+flowId);
}
return route;
}
@Override
public void removeVMRoute(int src, int dest, int flowId){
forwardingTable.removeRule(src, dest, flowId);
}
@Override
public void setRank(int rank) {
this.rank = rank;
}
@Override
public int getRank() {
return rank;
}
@Override
public void printVMRoute() {
forwardingTable.printForwardingTable(getName());
}
public String toString() {
return "Switch: "+this.getName();
}
@Override
public void addRoute(Node destHost, Link to) {
this.routingTable.addRoute(destHost, to);
}
@Override
public List getRoute(Node destHost) {
return this.routingTable.getRoute(destHost);
}
@Override
public RoutingTable getRoutingTable() {
return this.routingTable;
}
}