package edu.usc.enl.dynamicmeasurement.floodlight;
import edu.usc.enl.dynamicmeasurement.util.Util;
import edu.usc.enl.dynamicmeasurement.util.multithread.MultiThread;
import edu.usc.enl.dynamicmeasurement.util.profile.LatencyProfiler;
import edu.usc.enl.dynamicmeasurement.util.profile.Profilable;
import net.floodlightcontroller.core.IOFSwitch;
import net.floodlightcontroller.core.util.SingletonTask;
import org.openflow.protocol.OFMessage;
import org.openflow.protocol.statistics.OFFlowStatisticsReply;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* Created with IntelliJ IDEA.
* User: masoud
* Date: 10/18/13
* Time: 4:16 PM <br/>
* This is the responsible class for handling the configure-fetch loop (or say, fetch run save).
* In the beginning the controller installs just one rule at all switches.
* Once the controller sees a traffic on any of the switches it assumes that the traffic generators started.
* Thus even if there can be no tasks in the beginning, for the experiment to get started
* we need to send a kind of small traffic at time 0 of the experiment.
*/
class EpochProcedure implements Runnable, Profilable {
private final RuntimeTaskHandler runtimeTaskHandler;
private final int epochSize;
private MultiThread multiThread;
private int epoch;
private Map<String, SwitchData> switches;
private SingletonTask flowInfoTask;
// private long time0;
// private long time1 = 0;
private boolean seenTraffic = false;
private LatencyProfiler profiler;
EpochProcedure(RuntimeTaskHandler runtimeTaskHandler, int epochSize) {
this.runtimeTaskHandler = runtimeTaskHandler;
this.epochSize = epochSize;
multiThread = new MultiThread(Util.getSimulationConfiguration().getThreads());
epoch = 0;
createProfiler();
}
@Override
public void run() {
// long time = System.currentTimeMillis();
// System.out.println("Start=" + time);
reschedule();
try {
if (seenTraffic) {
long t = System.nanoTime();
profile("Fetch");
report();
profile("Run");
runtimeTaskHandler.step(switches.values(), epoch);
profile("Save");
//save rules
saveRules();
//wait for barrier of all switches
long currentTime = System.currentTimeMillis();
int passedBarrierSwitchesNum = 0;
double sendCheckDelayBudget = epochSize * 1000 / 2;
// System.out.println("Barrier wait numbers");
while (passedBarrierSwitchesNum < switches.size() && (System.currentTimeMillis() - currentTime) < sendCheckDelayBudget) {
for (SwitchData switchData : switches.values()) {
long nanoDelay = switchData.waitOnLastSend(10); //wait 1us
if (nanoDelay >= 0) {
// System.out.println(nanoDelay / 1000000.0 + "ms");
passedBarrierSwitchesNum++;
} else {
// System.out.println("not received for " + switchData.getStringId());
}
}
}
if (passedBarrierSwitchesNum < switches.size()) {
PeriodicReport.log.warn("Could not verify barrier before " + sendCheckDelayBudget + " only got for " + passedBarrierSwitchesNum);
}
profile(null);
System.out.println(epoch + "," + (System.nanoTime() - t) / 1e6);
//now write logs and do cleaning stuffs
runtimeTaskHandler.writeLogs(epoch);
writeProfiles();
runtimeTaskHandler.runEvents(epoch); //the eventrunner itself will +1 this
//get stats
// computeRuleLifeTimes();
epoch++;
if (epoch % 10 == 0) {
System.gc();
}
} else {
report();
for (SwitchData switchData : switches.values()) {
if (switchData.isTrafficStarted(10000 * epochSize)) {
seenTraffic = true;
System.out.println("Seen starting traffic from " + switchData);
break;
}
}
saveRules();
}
Util.flushAllControlledWriters();
} catch (Exception e) {
PeriodicReport.log.warn("Exception in report(): {}", e);
}
}
/**
* This is another way of computing the control loop delay.
* The control loop delay will be the measurement epoch - average rule lifetime at the switches
*/
private void computeRuleLifeTimes() {
double sum = 0;
int num = 0;
for (SwitchData switchData : switches.values()) {
for (OFFlowStatisticsReply statisticsReply : switchData.getLastFetchedFlows()) {
if (statisticsReply.getMatch().getNetworkSourceMaskLen() > 0) {
sum += statisticsReply.getDurationSeconds() + statisticsReply.getDurationNanoseconds() / 1E9;
num++;
}
}
}
if (num > 0) {
System.out.println(String.format(epoch + ": avgruleduration,%f,%d", sum / num, num));
}
}
private void reschedule() {
if (!runtimeTaskHandler.noEvent()) {
flowInfoTask.reschedule(epochSize, TimeUnit.SECONDS);
} else {
finish();
}
}
public boolean finished() {
return runtimeTaskHandler.noEvent();
}
private void saveRules() {
PeriodicReport.log.info("save ");
for (SwitchData switchData : switches.values()) {
multiThread.offer(switchData.saveRules(epoch));
}
multiThread.runJoin();
}
public void init(Map<String, SwitchData> switches, SingletonTask flowInfoTask) {
this.switches = switches;
this.flowInfoTask = flowInfoTask;
}
public void receive(IOFSwitch sw, OFMessage msg) {
// PeriodicReport.log.warn("Unhandled OF Message: {} from {}", msg, sw);
// pktIns++;
}
@Override
public void writeProfiles() {
if (profiler != null) {
profiler.write();
runtimeTaskHandler.writeProfiles();
}
}
@Override
public void createProfiler() {
this.profiler = new LatencyProfiler(getClass());
runtimeTaskHandler.createProfiler();
}
@Override
public void finishProfiler() {
if (profiler != null) {
profiler.finish();
runtimeTaskHandler.finishProfiler();
}
}
public void finish() {
runtimeTaskHandler.finish(epoch);
multiThread.finishThreads();
for (SwitchData switchData : switches.values()) {
switchData.finish();
}
finishProfiler();
}
protected void profile(String s) {
if (profiler != null) {
profiler.sequentialRecord(s);
}
}
private void report() {
for (final SwitchData switchData : switches.values()) {
multiThread.offer(switchData.getStats(epoch));
}
multiThread.runJoin();
}
}