| package net.onrc.onos.core.intent.runtime; |
| |
| import java.lang.ref.WeakReference; |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.WeakHashMap; |
| import java.util.concurrent.ExecutionException; |
| |
| import net.floodlightcontroller.core.IFloodlightProviderService; |
| import net.floodlightcontroller.core.IOFSwitch; |
| import net.floodlightcontroller.core.internal.OFMessageFuture; |
| import net.onrc.onos.core.flowprogrammer.IFlowPusherService; |
| import net.onrc.onos.core.intent.FlowEntry; |
| import net.onrc.onos.core.util.Dpid; |
| |
| import org.apache.commons.lang3.tuple.Pair; |
| import org.projectfloodlight.openflow.protocol.OFBarrierReply; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * This class is responsible for installing plans (lists of sets of FlowEntries) |
| * into local switches. In this context, a local switch is a switch for which |
| * this ONOS instance is the master. It also is responsible for sending barrier |
| * messages between sets. |
| */ |
| |
| public class PlanInstallRuntime { |
| |
| IFlowPusherService pusher; |
| IFloodlightProviderService provider; |
| private static final Logger log = LoggerFactory.getLogger(PlanInstallRuntime.class); |
| |
| /** |
| * Constructor. |
| * |
| * @param provider the FloodlightProviderService for list of local switches |
| * @param pusher the FlowPusherService to use for FlowEntry installation |
| */ |
| public PlanInstallRuntime(IFloodlightProviderService provider, |
| IFlowPusherService pusher) { |
| this.provider = provider; |
| this.pusher = pusher; |
| } |
| |
| /** |
| * This class is a temporary class for collecting FlowMod installation |
| * information. It is largely used for debugging purposes, and it should not |
| * be depended on for other purposes. |
| * <p> |
| * TODO: This class should be wrapped into a more generic debugging |
| * framework when available. |
| */ |
| private static class FlowModCount { |
| WeakReference<IOFSwitch> sw; |
| long modFlows = 0; |
| long delFlows = 0; |
| long errors = 0; |
| |
| /** |
| * Constructor. |
| * |
| * @param sw the switch for FlowMod statistics collection |
| */ |
| FlowModCount(IOFSwitch sw) { |
| this.sw = new WeakReference<>(sw); |
| } |
| |
| /** |
| * Include the FlowEntry in this switch statistics object. |
| * |
| * @param entry the FlowEntry to count |
| */ |
| void addFlowEntry(FlowEntry entry) { |
| switch (entry.getOperator()) { |
| case ADD: |
| modFlows++; |
| break; |
| case ERROR: |
| errors++; |
| break; |
| case REMOVE: |
| delFlows++; |
| break; |
| default: |
| break; |
| } |
| } |
| |
| /** |
| * Returns a string representation of this object. |
| * |
| * @return string representation of this object |
| */ |
| @Override |
| public String toString() { |
| final IOFSwitch swTemp = sw.get(); |
| return "sw:" + ((swTemp == null) ? "disconnected" : swTemp.getStringId()) |
| + ": modify " + modFlows + " delete " + delFlows + " error " + errors; |
| } |
| |
| static Map<IOFSwitch, FlowModCount> map = new WeakHashMap<>(); |
| |
| /** |
| * This function is used for collecting statistics information. It |
| * should be called for every FlowEntry that is pushed to the switch for |
| * accurate statistics. |
| * <p> |
| * This class maintains a map of Switches and FlowModCount collection |
| * objects, which are used for collection. |
| * <p> |
| * TODO: This should be refactored to use a more generic mechanism when |
| * available. |
| * |
| * @param sw the switch that entry is being pushed to |
| * @param entry the FlowEntry being pushed |
| */ |
| static void countFlowEntry(IOFSwitch sw, FlowEntry entry) { |
| FlowModCount count = map.get(sw); |
| if (count == null) { |
| count = new FlowModCount(sw); |
| map.put(sw, count); |
| } |
| count.addFlowEntry(entry); |
| } |
| |
| /** |
| * Reset the statistics collection. It should be called when required |
| * for debugging. |
| */ |
| static void startCount() { |
| map.clear(); |
| } |
| |
| /** |
| * Print out the statistics information when required for debugging. |
| */ |
| static void printCount() { |
| StringBuilder result = new StringBuilder(); |
| |
| result.append("FLOWMOD COUNT:\n"); |
| for (FlowModCount count : map.values()) { |
| result.append(count.toString() + '\n'); |
| } |
| if (map.values().isEmpty()) { |
| result.append("No flow mods installed\n"); |
| } |
| log.debug(result.toString()); |
| } |
| } |
| |
| /** |
| * This function should be called to install the FlowEntries in the plan. |
| * <p> |
| * Each set of FlowEntries can be installed together, but all entries should |
| * be installed proceeded to the next set. |
| * <p> |
| * TODO: This method lack coordination between the other ONOS instances |
| * before proceeded with the next set of entries |
| * |
| * @param plan list of set of FlowEntries for installation on local switches |
| * @return true (we assume installation is successful) |
| */ |
| public boolean installPlan(List<Set<FlowEntry>> plan) { |
| long start = System.nanoTime(); |
| Map<Long, IOFSwitch> switches = provider.getSwitches(); |
| |
| log.debug("IOFSwitches: {}", switches); |
| FlowModCount.startCount(); |
| for (Set<FlowEntry> phase : plan) { |
| Set<Pair<Dpid, FlowEntry>> entries = new HashSet<>(); |
| Set<IOFSwitch> modifiedSwitches = new HashSet<>(); |
| |
| long step1 = System.nanoTime(); |
| // convert flow entries and create pairs |
| for (FlowEntry entry : phase) { |
| IOFSwitch sw = switches.get(entry.getSwitch()); |
| if (sw == null) { |
| // no active switch, skip this flow entry |
| log.debug("Skipping flow entry: {}", entry); |
| continue; |
| } |
| entries.add(Pair.of(new Dpid(entry.getSwitch()), entry)); |
| modifiedSwitches.add(sw); |
| FlowModCount.countFlowEntry(sw, entry); |
| } |
| long step2 = System.nanoTime(); |
| |
| // push flow entries to switches |
| log.debug("Pushing flow entries: {}", entries); |
| pusher.pushFlowEntries(entries); |
| long step3 = System.nanoTime(); |
| |
| // insert a barrier after each phase on each modifiedSwitch |
| // wait for confirmation messages before proceeding |
| List<Pair<IOFSwitch, OFMessageFuture<OFBarrierReply>>> barriers = new ArrayList<>(); |
| for (IOFSwitch sw : modifiedSwitches) { |
| barriers.add(Pair.of(sw, pusher.barrierAsync(new Dpid(sw.getId())))); |
| } |
| for (Pair<IOFSwitch, OFMessageFuture<OFBarrierReply>> pair : barriers) { |
| IOFSwitch sw = pair.getLeft(); |
| OFMessageFuture<OFBarrierReply> future = pair.getRight(); |
| try { |
| future.get(); |
| } catch (InterruptedException | ExecutionException e) { |
| log.error("Barrier message not received for sw: {}", sw); |
| } |
| } |
| long step4 = System.nanoTime(); |
| log.debug("MEASUREMENT: convert: {} ns, push: {} ns, barrierWait: {} ns", |
| step2 - step1, step3 - step2, step4 - step3); |
| |
| } |
| long end = System.nanoTime(); |
| log.debug("MEASUREMENT: Install plan: {} ns", (end - start)); |
| FlowModCount.printCount(); |
| |
| // TODO: we assume that the plan installation succeeds for now |
| return true; |
| } |
| } |