package net.onrc.onos.core.flowprogrammer; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; import net.floodlightcontroller.core.IOFSwitch; import net.onrc.onos.core.util.Dpid; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * FlowSynchronizer is an implementation of FlowSyncService. * In addition to IFlowSyncService, FlowSynchronizer periodically reads flow * tables from switches and compare them with GraphDB to drop unnecessary * flows and/or to install missing flows. */ public class FlowSynchronizer implements IFlowSyncService { private static Logger log = LoggerFactory.getLogger(FlowSynchronizer.class); // TODO: fix when FlowSynchronizer is refactored // private DBOperation dbHandler; protected IFlowPusherService pusher; private Map<IOFSwitch, FutureTask<SyncResult>> switchThreads; public FlowSynchronizer() { // TODO: fix when FlowSynchronizer is refactored // dbHandler = GraphDBManager.getDBOperation(); switchThreads = new HashMap<IOFSwitch, FutureTask<SyncResult>>(); } @Override public Future<SyncResult> synchronize(IOFSwitch sw) { Synchronizer sync = new Synchronizer(sw); FutureTask<SyncResult> task = new FutureTask<SyncResult>(sync); switchThreads.put(sw, task); task.run(); return task; } @Override public void interrupt(IOFSwitch sw) { FutureTask<SyncResult> t = switchThreads.remove(sw); if (t != null) { t.cancel(true); } } /** * Initialize Synchronizer. * * @param pusherService FlowPusherService used for sending messages. */ public void init(IFlowPusherService pusherService) { pusher = pusherService; } /** * Synchronizer represents main thread of synchronization. */ protected class Synchronizer implements Callable<SyncResult> { IOFSwitch sw; // TODO: fix when FlowSynchronizer is refactored // ISwitchObject swObj; public Synchronizer(IOFSwitch sw) { this.sw = sw; // TODO: fix when FlowSynchronizer is refactored // Dpid dpid = new Dpid(sw.getId()); // this.swObj = dbHandler.searchSwitch(dpid.toString()); } double graphIDTime, switchTime, compareTime, graphEntryTime, extractTime, pushTime, totalTime; @Override public SyncResult call() { pusher.suspend(new Dpid(sw.getId())); try { long start = System.nanoTime(); Set<FlowEntryWrapper> graphEntries = getFlowEntriesFromGraph(); long step1 = System.nanoTime(); Set<FlowEntryWrapper> switchEntries = getFlowEntriesFromSwitch(); long step2 = System.nanoTime(); SyncResult result = compare(graphEntries, switchEntries); long step3 = System.nanoTime(); graphIDTime = (step1 - start); switchTime = (step2 - step1); compareTime = (step3 - step2); totalTime = (step3 - start); outputTime(); return result; } finally { pusher.resume(new Dpid(sw.getId())); } } private void outputTime() { double div = Math.pow(10, 6); //convert nanoseconds to ms graphIDTime /= div; switchTime /= div; compareTime = (compareTime - graphEntryTime - extractTime - pushTime) / div; graphEntryTime /= div; extractTime /= div; pushTime /= div; totalTime /= div; log.debug("Sync time (ms):{},{},{},{},{},{},{}" , graphIDTime , switchTime , compareTime , graphEntryTime , extractTime , pushTime , totalTime); } /** * Compare flows entries in GraphDB and switch to pick up necessary * messages. * After picking up, picked messages are added to FlowPusher. * * @param graphEntries Flow entries in GraphDB. * @param switchEntries Flow entries in switch. */ private SyncResult compare(Set<FlowEntryWrapper> graphEntries, Set<FlowEntryWrapper> switchEntries) { int added = 0, removed = 0, skipped = 0; for (FlowEntryWrapper entry : switchEntries) { if (graphEntries.contains(entry)) { graphEntries.remove(entry); skipped++; } else { // remove flow entry from the switch entry.removeFromSwitch(sw); removed++; } } for (FlowEntryWrapper entry : graphEntries) { // add flow entry to switch entry.addToSwitch(sw); graphEntryTime += entry.dbTime; extractTime += entry.extractTime; pushTime += entry.pushTime; added++; } log.debug("Flow entries added {}, " + "Flow entries removed {}, " + "Flow entries skipped {}" , added , removed , skipped); return new SyncResult(added, removed, skipped); } /** * Read GraphDB to get FlowEntries associated with a switch. * * @return set of FlowEntries */ private Set<FlowEntryWrapper> getFlowEntriesFromGraph() { Set<FlowEntryWrapper> entries = new HashSet<FlowEntryWrapper>(); // TODO: fix when FlowSynchronizer is refactored /* for(IFlowEntry entry : swObj.getFlowEntries()) { FlowEntryWrapper fe = new FlowEntryWrapper(entry); entries.add(fe); } */ return entries; } /** * Read flow table from switch and derive FlowEntries from table. * * @return set of FlowEntries */ private Set<FlowEntryWrapper> getFlowEntriesFromSwitch() { /*int lengthU = 0; OFMatch match = new OFMatch(); match.setWildcards(OFMatch.OFPFW_ALL); OFFlowStatisticsRequest stat = new OFFlowStatisticsRequest(); stat.setOutPort((short) 0xffff); //TODO: OFPort.OFPP_NONE stat.setTableId((byte) 0xff); // TODO: fix this with enum (ALL TABLES) stat.setMatch(match); List<OFStatistics> stats = new ArrayList<OFStatistics>(); stats.add(stat); lengthU += stat.getLength(); OFStatisticsRequest req = new OFStatisticsRequest(); req.setStatisticType(OFStatisticsType.FLOW); req.setStatistics(stats); lengthU += req.getLengthU(); req.setLengthU(lengthU);*/ //List<OFStatistics> entries = null; // XXX S when we fix stats, we fix this /*try { Future<List<OFStatistics>> dfuture = sw.getStatistics(req); entries = dfuture.get(); } catch (IOException e) { log.error("Error getting statistics", e); return null; } catch (InterruptedException e) { log.error("Error getting statistics", e); return null; } catch (ExecutionException e) { log.error("Error getting statistics", e); return null; }*/ Set<FlowEntryWrapper> results = new HashSet<FlowEntryWrapper>(); /* for (OFStatistics result : entries) { OFFlowStatisticsReply entry = (OFFlowStatisticsReply) result; FlowEntryWrapper fe = new FlowEntryWrapper(entry); results.add(fe); } */ return results; } } /** * FlowEntryWrapper represents abstract FlowEntry which is embodied * by FlowEntryId (from GraphDB) or OFFlowStatisticsReply (from switch). */ static class FlowEntryWrapper { //FlowEntryId flowEntryId; // TODO: fix when FlowSynchronizer is refactored // IFlowEntry iFlowEntry; //OFFlowStatisticsReply statisticsReply; // TODO: fix when FlowSynchronizer is refactored /* public FlowEntryWrapper(IFlowEntry entry) { flowEntryId = new FlowEntryId(entry.getFlowEntryId()); iFlowEntry = entry; } */ /*public FlowEntryWrapper(OFFlowStatisticsReply entry) { flowEntryId = new FlowEntryId(entry.getCookie()); statisticsReply = entry; }*/ /** * Install this FlowEntry to a switch via FlowPusher. * * @param sw Switch to which flow will be installed. */ double dbTime, extractTime, pushTime; public void addToSwitch(IOFSwitch sw) { /*if (statisticsReply != null) { log.error("Error adding existing flow entry {} to sw {}", statisticsReply.getCookie(), sw.getId()); return; } double startDB = System.nanoTime();*/ // Get the Flow Entry state from the Network Graph // TODO: fix when FlowSynchronizer is refactored /* if (iFlowEntry == null) { try { // TODO: fix when FlowSynchronizer is refactored iFlowEntry = dbHandler.searchFlowEntry(flowEntryId); } catch (Exception e) { log.error("Error finding flow entry {} in Network Graph", flowEntryId); return; } } */ //dbTime = System.nanoTime() - startDB; // // TODO: The old FlowDatabaseOperation class is gone, so the code // /* double startExtract = System.nanoTime(); FlowEntry flowEntry = FlowDatabaseOperation.extractFlowEntry(iFlowEntry); if (flowEntry == null) { log.error("Cannot add flow entry {} to sw {} : flow entry cannot be extracted", flowEntryId, sw.getId()); return; } extractTime = System.nanoTime() - startExtract; double startPush = System.nanoTime(); pusher.pushFlowEntry(sw, flowEntry, MsgPriority.HIGH); pushTime = System.nanoTime() - startPush; */ } /** * Remove this FlowEntry from a switch via FlowPusher. * * @param sw Switch from which flow will be removed. */ public void removeFromSwitch(IOFSwitch sw) { /*if (statisticsReply == null) { log.error("Error removing non-existent flow entry {} from sw {}", flowEntryId, sw.getId()); return; } // Convert Statistics Reply to Flow Mod, then write it OFFlowMod fm = new OFFlowMod(); fm.setCookie(statisticsReply.getCookie()); fm.setCommand(OFFlowMod.OFPFC_DELETE_STRICT); fm.setLengthU(OFFlowMod.MINIMUM_LENGTH); fm.setMatch(statisticsReply.getMatch()); fm.setPriority(statisticsReply.getPriority()); fm.setOutPort(OFPort.OFPP_NONE);*/ // XXX BOC commented out pending FlowSync refactor //pusher.add(sw, fm, MsgPriority.HIGH); } /** * Return the hash code of the Flow Entry ID. */ @Override public int hashCode() { //return flowEntryId.hashCode(); return 0; } /** * Returns true of the object is another Flow Entry ID with * the same value; otherwise, returns false. * * @param obj to compare * @return true if the object has the same Flow Entry ID. */ @Override public boolean equals(Object obj) { if (obj != null && obj.getClass() == this.getClass()) { //FlowEntryWrapper entry = (FlowEntryWrapper) obj; // TODO: we need to actually compare the match + actions //return this.flowEntryId.equals(entry.flowEntryId); return true; } return false; } @Override public String toString() { //return flowEntryId.toString(); return ""; } } }