diff --git a/src/main/java/net/floodlightcontroller/routing/IRoutingService.java b/src/main/java/net/floodlightcontroller/routing/IRoutingService.java index 76c8d56e66f6bd75fcc2b0c0cf202fb4b2ae2808..be6c98e90764058424cf8ae8c819131fbe8d5862 100644 --- a/src/main/java/net/floodlightcontroller/routing/IRoutingService.java +++ b/src/main/java/net/floodlightcontroller/routing/IRoutingService.java @@ -45,7 +45,7 @@ public interface IRoutingService extends IFloodlightService { } }; - public PATH_METRIC setPathMetric(PATH_METRIC metric); + public void setPathMetric(PATH_METRIC metric); public PATH_METRIC getPathMetric(); /** @@ -76,7 +76,7 @@ public interface IRoutingService extends IFloodlightService { * @param dstPort destination port on destination switch * @return the lowest cost path */ - public Route getPath(DatapathId srcId, OFPort srcPort, DatapathId dstId, OFPort dstPort); + public Route getPath(DatapathId src, OFPort srcPort, DatapathId dst, OFPort dstPort); /** * Return all possible paths up to quantity of the globally configured max. @@ -123,7 +123,7 @@ public interface IRoutingService extends IFloodlightService { * @param numReqPaths the requested quantity of paths * @return list of paths ordered least to greatest cost */ - public List<Route> getPathsSlow(DatapathId srcDpid, DatapathId dstDpid, int numReqPaths); + public List<Route> getPathsSlow(DatapathId src, DatapathId dst, int numReqPaths); /** * Check if a path exists between src and dst diff --git a/src/main/java/net/floodlightcontroller/routing/RoutingManager.java b/src/main/java/net/floodlightcontroller/routing/RoutingManager.java new file mode 100644 index 0000000000000000000000000000000000000000..f5d8c42c6f98f8e417d104ed3b3cccbcbb2cd7ac --- /dev/null +++ b/src/main/java/net/floodlightcontroller/routing/RoutingManager.java @@ -0,0 +1,110 @@ +package net.floodlightcontroller.routing; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.projectfloodlight.openflow.types.DatapathId; +import org.projectfloodlight.openflow.types.OFPort; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; + +import net.floodlightcontroller.core.module.FloodlightModuleContext; +import net.floodlightcontroller.core.module.FloodlightModuleException; +import net.floodlightcontroller.core.module.IFloodlightModule; +import net.floodlightcontroller.core.module.IFloodlightService; +import net.floodlightcontroller.topology.ITopologyManagerBackend; +import net.floodlightcontroller.topology.ITopologyService; + +/** + * Separate path-finding and routing functionality from the + * topology package. It makes sense to keep much of the core + * code in the TopologyInstance, but the TopologyManger is + * too confusing implementing so many interfaces and doing + * so many tasks. This is a cleaner approach IMHO. + * + * @author rizard + */ +public class RoutingManager implements IFloodlightModule, IRoutingService { + private Logger log = LoggerFactory.getLogger(RoutingManager.class); + + private static ITopologyManagerBackend tm; + + @Override + public Collection<Class<? extends IFloodlightService>> getModuleServices() { + return ImmutableSet.of(IRoutingService.class); + } + + @Override + public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() { + return ImmutableMap.of(IRoutingService.class, this); + } + + @Override + public Collection<Class<? extends IFloodlightService>> getModuleDependencies() { + return ImmutableSet.of(ITopologyService.class); + } + + @Override + public void init(FloodlightModuleContext context) throws FloodlightModuleException { + log.debug("RoutingManager starting up"); + tm = (ITopologyManagerBackend) context.getServiceImpl(ITopologyService.class); + } + + @Override + public void startUp(FloodlightModuleContext context) throws FloodlightModuleException { } + + @Override + public void setPathMetric(PATH_METRIC metric) { + tm.setPathMetric(metric); + } + + @Override + public PATH_METRIC getPathMetric() { + return tm.getPathMetric(); + } + + + @Override + public void setMaxPathsToCompute(int max) { + tm.setMaxPathsToCompute(max); + } + + @Override + public int getMaxPathsToCompute() { + return tm.getMaxPathsToCompute(); + } + + @Override + public Route getPath(DatapathId src, DatapathId dst) { + return tm.getCurrentTopologyInstance().getPath(src, dst); + } + + @Override + public Route getPath(DatapathId src, OFPort srcPort, DatapathId dst, OFPort dstPort) { + return tm.getCurrentTopologyInstance().getPath(src, srcPort, dst, dstPort); + } + + @Override + public List<Route> getPathsFast(DatapathId src, DatapathId dst) { + return tm.getCurrentTopologyInstance().getPathsFast(src, dst, tm.getMaxPathsToCompute()); + } + + @Override + public List<Route> getPathsFast(DatapathId src, DatapathId dst, int numReqPaths) { + return tm.getCurrentTopologyInstance().getPathsFast(src, dst, numReqPaths); + } + + @Override + public List<Route> getPathsSlow(DatapathId src, DatapathId dst, int numReqPaths) { + return tm.getCurrentTopologyInstance().getPathsSlow(src, dst, numReqPaths); + } + + @Override + public boolean pathExists(DatapathId src, DatapathId dst) { + return tm.getCurrentTopologyInstance().pathExists(src, dst); + } +} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/routing/web/PathMetricsResource.java b/src/main/java/net/floodlightcontroller/routing/web/PathMetricsResource.java index 73fe3f7542d0a6b8c3ddd08fad2b8968779fa44b..cf032a135520f0395eefa7a405409f673af901d8 100644 --- a/src/main/java/net/floodlightcontroller/routing/web/PathMetricsResource.java +++ b/src/main/java/net/floodlightcontroller/routing/web/PathMetricsResource.java @@ -63,12 +63,8 @@ public class PathMetricsResource extends ServerResource { return Collections.singletonMap("error", "invalid path metric " + metric); } - if (routing.setPathMetric(type) != type) { - log.error("Failed to set valid path metric {}. Bug?", type.getMetricName()); - return Collections.singletonMap("error", "failed to set valid path metric " + type.getMetricName()); - } - - log.debug("Set path metric to {}", type.getMetricName()); + log.debug("Setting path metric to {}", type.getMetricName()); + routing.setPathMetric(type); return Collections.singletonMap("success", "path metric set to " + type.getMetricName()); } } \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/topology/ITopologyManagerBackend.java b/src/main/java/net/floodlightcontroller/topology/ITopologyManagerBackend.java new file mode 100644 index 0000000000000000000000000000000000000000..64c4e06a2abcf3581fa23e5a99a96503d92d727d --- /dev/null +++ b/src/main/java/net/floodlightcontroller/topology/ITopologyManagerBackend.java @@ -0,0 +1,13 @@ +package net.floodlightcontroller.topology; + +import net.floodlightcontroller.routing.IRoutingService.PATH_METRIC; + +public interface ITopologyManagerBackend { + public TopologyInstance getCurrentTopologyInstance(); + + public PATH_METRIC getPathMetric(); + public void setPathMetric(PATH_METRIC metric); + + public int getMaxPathsToCompute(); + public void setMaxPathsToCompute(int max); +} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java b/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java index 318ba23523beaa0c70507effd0c9374516e81fd2..d3f50f404f74a89d822ab6ce20c022d7f4b3b945 100644 --- a/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java +++ b/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java @@ -80,7 +80,7 @@ public class TopologyInstance { private Map<DatapathId, Set<NodePortTuple>> portsBroadcastPerArchipelago; private Map<RouteId, List<Route>> pathcache; /* contains computed paths ordered best to worst */ - protected TopologyInstance(Map<DatapathId, Set<OFPort>> portsWithLinks, + public TopologyInstance(Map<DatapathId, Set<OFPort>> portsWithLinks, Set<NodePortTuple> portsBlocked, Map<NodePortTuple, Set<Link>> linksNonBcastNonTunnel, Set<NodePortTuple> portsWithMoreThanTwoLinks, @@ -138,7 +138,7 @@ public class TopologyInstance { this.archipelagoFromCluster = new HashMap<Cluster, Archipelago>(); } - protected void compute() { + public void compute() { /* * Step 1: Compute clusters ignoring ports with > 2 links and * blocked links. @@ -185,7 +185,7 @@ public class TopologyInstance { /* * Checks if OF port is edge port */ - protected boolean isEdge(DatapathId sw, OFPort portId) { + public boolean isEdge(DatapathId sw, OFPort portId) { NodePortTuple np = new NodePortTuple(sw, portId); if (links.get(np) == null || links.get(np).isEmpty()) { return true; @@ -198,7 +198,7 @@ public class TopologyInstance { /* * Returns broadcast ports for the given DatapathId */ - protected Set<OFPort> swBroadcastPorts(DatapathId sw) { + public Set<OFPort> swBroadcastPorts(DatapathId sw) { if (!portsBroadcastPerSwitch.containsKey(sw) || portsBroadcastPerSwitch.get(sw) == null) { log.debug("Could not locate broadcast ports for switch {}", sw); return Collections.emptySet(); @@ -402,11 +402,11 @@ public class TopologyInstance { return currIndex; } - protected Set<NodePortTuple> getBlockedPorts() { + public Set<NodePortTuple> getBlockedPorts() { return this.portsBlocked; } - protected Set<Link> getBlockedLinks() { + public Set<Link> getBlockedLinks() { return this.linksBlocked; } @@ -415,33 +415,33 @@ public class TopologyInstance { * @param l * @return */ - protected boolean isBlockedLink(Link l) { + public boolean isBlockedLink(Link l) { NodePortTuple n1 = new NodePortTuple(l.getSrc(), l.getSrcPort()); NodePortTuple n2 = new NodePortTuple(l.getDst(), l.getDstPort()); return (isBlockedPort(n1) || isBlockedPort(n2)); } - protected boolean isBlockedPort(NodePortTuple npt) { + public boolean isBlockedPort(NodePortTuple npt) { return portsBlocked.contains(npt); } - protected boolean isTunnelPort(NodePortTuple npt) { + public boolean isTunnelPort(NodePortTuple npt) { return portsTunnel.contains(npt); } - protected boolean isTunnelLink(Link l) { + public boolean isTunnelLink(Link l) { NodePortTuple n1 = new NodePortTuple(l.getSrc(), l.getSrcPort()); NodePortTuple n2 = new NodePortTuple(l.getDst(), l.getDstPort()); return (isTunnelPort(n1) || isTunnelPort(n2)); } - protected boolean isBroadcastLink(Link l) { + public boolean isBroadcastLink(Link l) { NodePortTuple n1 = new NodePortTuple(l.getSrc(), l.getSrcPort()); NodePortTuple n2 = new NodePortTuple(l.getDst(), l.getDstPort()); return (isBroadcastPort(n1) || isBroadcastPort(n2)); } - protected boolean isBroadcastPort(NodePortTuple npt) { + public boolean isBroadcastPort(NodePortTuple npt) { return portsBroadcastAll.contains(npt); } @@ -647,7 +647,7 @@ public class TopologyInstance { /* * Creates a map of links and the cost associated with each link */ - protected Map<Link,Integer> initLinkCostMap() { + public Map<Link,Integer> initLinkCostMap() { Map<Link, Integer> linkCost = new HashMap<Link, Integer>(); long rawLinkSpeed; int linkSpeedMBps; @@ -821,7 +821,7 @@ public class TopologyInstance { * Getter Functions */ - protected boolean pathExists(DatapathId srcId, DatapathId dstId) { + public boolean pathExists(DatapathId srcId, DatapathId dstId) { Archipelago srcA = getArchipelago(srcId); Archipelago dstA = getArchipelago(dstId); if (!srcA.getId().equals(dstA.getId())) { @@ -876,7 +876,7 @@ public class TopologyInstance { * @param k: The number of routes that you want. Must be positive integer. * @return ArrayList of Routes or null if bad parameters */ - protected List<Route> getPathsFast(DatapathId src, DatapathId dst, int k) { + public List<Route> getPathsFast(DatapathId src, DatapathId dst, int k) { RouteId routeId = new RouteId(src, dst); List<Route> routes = pathcache.get(routeId); @@ -903,7 +903,7 @@ public class TopologyInstance { * @param k: The number of routes that you want. Must be positive integer. * @return ArrayList of Routes or null if bad parameters */ - protected List<Route> getPathsSlow(DatapathId src, DatapathId dst, int k) { + public List<Route> getPathsSlow(DatapathId src, DatapathId dst, int k) { RouteId routeId = new RouteId(src, dst); List<Route> routes = pathcache.get(routeId); @@ -926,7 +926,7 @@ public class TopologyInstance { return null; } - protected void setPathCosts(Route r) { + public void setPathCosts(Route r) { U64 cost = U64.ZERO; // Set number of hops. Assuming the list of NPTs is always even. @@ -1169,7 +1169,7 @@ public class TopologyInstance { * @param dstPort * @return */ - protected Route getPath(DatapathId srcId, OFPort srcPort, + public Route getPath(DatapathId srcId, OFPort srcPort, DatapathId dstId, OFPort dstPort) { // Return null if the route source and destination are the // same switch ports. @@ -1207,7 +1207,7 @@ public class TopologyInstance { * @return */ - protected Route getPath(DatapathId srcId, DatapathId dstId) { + public Route getPath(DatapathId srcId, DatapathId dstId) { // Return null route if srcId equals dstId if (srcId.equals(dstId)) return null; @@ -1232,7 +1232,7 @@ public class TopologyInstance { // ITopologyService interface method helpers. // - protected boolean isInternalLinkInCluster(DatapathId switchid, OFPort port) { + public boolean isInternalLinkInCluster(DatapathId switchid, OFPort port) { return !isAttachmentPointPort(switchid, port); } @@ -1244,7 +1244,7 @@ public class TopologyInstance { return true; } - protected DatapathId getClusterId(DatapathId switchId) { + public DatapathId getClusterId(DatapathId switchId) { Cluster c = clustersPerSwitch.get(switchId); if (c != null) { return c.getId(); @@ -1252,7 +1252,7 @@ public class TopologyInstance { return switchId; } - protected DatapathId getArchipelagoId(DatapathId switchId) { + public DatapathId getArchipelagoId(DatapathId switchId) { Cluster c = clustersPerSwitch.get(switchId); if (c != null) { return archipelagoFromCluster.get(c).getId(); @@ -1260,7 +1260,7 @@ public class TopologyInstance { return switchId; } - protected Set<DatapathId> getSwitchesInCluster(DatapathId switchId) { + public Set<DatapathId> getSwitchesInCluster(DatapathId switchId) { Cluster c = clustersPerSwitch.get(switchId); if (c != null) { return c.getNodes(); @@ -1269,7 +1269,7 @@ public class TopologyInstance { return ImmutableSet.of(switchId); } - protected boolean isInSameCluster(DatapathId switch1, DatapathId switch2) { + public boolean isInSameCluster(DatapathId switch1, DatapathId switch2) { Cluster c1 = clustersPerSwitch.get(switch1); Cluster c2 = clustersPerSwitch.get(switch2); if (c1 != null && c2 != null) { @@ -1278,14 +1278,14 @@ public class TopologyInstance { return (switch1.equals(switch2)); } - protected boolean isNotBlocked(DatapathId sw, OFPort portId) { + public boolean isNotBlocked(DatapathId sw, OFPort portId) { return !isBlockedPort(new NodePortTuple(sw, portId)); } /* * Takes finiteBroadcastTree into account to prevent loops in the network */ - protected boolean isBroadcastAllowedOnSwitchPort(DatapathId sw, OFPort portId) { + public boolean isBroadcastAllowedOnSwitchPort(DatapathId sw, OFPort portId) { if (!isEdge(sw, portId)){ NodePortTuple npt = new NodePortTuple(sw, portId); if (portsBroadcastAll.contains(npt)) @@ -1295,12 +1295,12 @@ public class TopologyInstance { return true; } - protected boolean isConsistent(DatapathId oldSw, OFPort oldPort, DatapathId newSw, OFPort newPort) { + public boolean isConsistent(DatapathId oldSw, OFPort oldPort, DatapathId newSw, OFPort newPort) { if (isInternalLinkInCluster(newSw, newPort)) return true; return (oldSw.equals(newSw) && oldPort.equals(newPort)); } - protected boolean isInSameArchipelago(DatapathId s1, DatapathId s2) { + public boolean isInSameArchipelago(DatapathId s1, DatapathId s2) { for (Archipelago a : archipelagos) { if (a.getSwitches().contains(s1) && a.getSwitches().contains(s2)) { return true; @@ -1309,15 +1309,15 @@ public class TopologyInstance { return false; } - protected Set<DatapathId> getSwitches() { + public Set<DatapathId> getSwitches() { return switches; } - protected Set<OFPort> getPortsWithLinks(DatapathId sw) { + public Set<OFPort> getPortsWithLinks(DatapathId sw) { return portsWithLinks.get(sw); } - protected Set<OFPort> getBroadcastPorts(DatapathId targetSw, DatapathId src, OFPort srcPort) { + public Set<OFPort> getBroadcastPorts(DatapathId targetSw, DatapathId src, OFPort srcPort) { Set<OFPort> result = new HashSet<OFPort>(); DatapathId clusterId = getClusterId(targetSw); for (NodePortTuple npt : clusterPorts.get(clusterId)) { @@ -1328,15 +1328,15 @@ public class TopologyInstance { return result; } - protected Set<Link> getInternalInterClusterLinks() { + public Set<Link> getInternalInterClusterLinks() { return linksNonExternalInterCluster; } - protected Set<NodePortTuple> getAllBroadcastPorts() { + public Set<NodePortTuple> getAllBroadcastPorts() { return portsBroadcastAll; } - protected Set<DatapathId> getClusterIdsInArchipelago(DatapathId sw) { + public Set<DatapathId> getClusterIdsInArchipelago(DatapathId sw) { Archipelago a = getArchipelago(sw); if (a != null) { return a.getClusters().stream().map(c -> c.getId()).collect(Collectors.toSet()); @@ -1406,7 +1406,7 @@ public class TopologyInstance { } } - protected Set<NodePortTuple> getBroadcastPortsInArchipelago(DatapathId sw) { + public Set<NodePortTuple> getBroadcastPortsInArchipelago(DatapathId sw) { Archipelago a = getArchipelago(sw); if (a != null) { return portsBroadcastPerArchipelago.get(a.getId()); @@ -1414,7 +1414,7 @@ public class TopologyInstance { return ImmutableSet.of(); } - protected Set<DatapathId> getArchipelagoIds() { + public Set<DatapathId> getArchipelagoIds() { return archipelagos.stream().map(a -> a.getId()).collect(Collectors.toSet()); } } \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/topology/TopologyManager.java b/src/main/java/net/floodlightcontroller/topology/TopologyManager.java index f5ad1592d488bb8c6ad85653db38c253c147c5f4..d5b1729c87838e12dfb79fcd8a99c6426fd0a2c3 100644 --- a/src/main/java/net/floodlightcontroller/topology/TopologyManager.java +++ b/src/main/java/net/floodlightcontroller/topology/TopologyManager.java @@ -34,7 +34,7 @@ import net.floodlightcontroller.packet.Ethernet; import net.floodlightcontroller.packet.LLDP; import net.floodlightcontroller.restserver.IRestApiService; import net.floodlightcontroller.routing.IRoutingService; -import net.floodlightcontroller.routing.Route; +import net.floodlightcontroller.routing.IRoutingService.PATH_METRIC; import net.floodlightcontroller.routing.web.RoutingWebRoutable; import net.floodlightcontroller.statistics.IStatisticsService; import net.floodlightcontroller.threadpool.IThreadPoolService; @@ -62,1115 +62,1079 @@ import java.util.concurrent.TimeUnit; * of the network graph, as well as implementing tools for finding routes * through the topology. */ -public class TopologyManager implements IFloodlightModule, ITopologyService, IRoutingService, ILinkDiscoveryListener, IOFMessageListener { - protected static Logger log = LoggerFactory.getLogger(TopologyManager.class); - public static final String MODULE_NAME = "topology"; - - protected static IStatisticsService statisticsService; - - protected static volatile PATH_METRIC pathMetric = PATH_METRIC.HOPCOUNT_AVOID_TUNNELS; //default: compute paths on hop count - protected static boolean collectStatistics = false; - - /** - * Maximum number of route entries stored in memory. - */ - private static volatile int maxPathsToCompute = 3; - - /** - * Role of the controller. - */ - private HARole role; - - /** - * Set of ports for each switch - */ - protected Map<DatapathId, Set<OFPort>> switchPorts; - - /** - * Set of links organized by node port tuple - */ - protected Map<NodePortTuple, Set<Link>> switchPortLinks; - - /** - * Set of direct links - */ - protected Map<NodePortTuple, Set<Link>> directLinks; - - /** - * set of links that are broadcast domain links. - */ - protected Map<NodePortTuple, Set<Link>> interClusterLinks; - - /** - * set of tunnel links - */ - protected Set<NodePortTuple> tunnelPorts; - - protected ILinkDiscoveryService linkDiscoveryService; - protected IThreadPoolService threadPoolService; - protected IFloodlightProviderService floodlightProviderService; - protected IOFSwitchService switchService; - protected IRestApiService restApiService; - protected IDebugCounterService debugCounterService; - - // Modules that listen to our updates - protected ArrayList<ITopologyListener> topologyAware; - - protected BlockingQueue<LDUpdate> ldUpdates; - - // These must be accessed using getCurrentInstance(), not directly - protected TopologyInstance currentInstance; - - protected SingletonTask newInstanceTask; - private Date lastUpdateTime; - - /** - * Flag that indicates if links (direct/tunnel/multihop links) were - * updated as part of LDUpdate. - */ - protected boolean linksUpdated; - /** - * Flag that indicates if direct or tunnel links were updated as - * part of LDUpdate. - */ - protected boolean dtLinksUpdated; - - /** Flag that indicates if tunnel ports were updated or not - */ - protected boolean tunnelPortsUpdated; - - protected int TOPOLOGY_COMPUTE_INTERVAL_MS = 500; - - private IHAListener haListener; - - /** - * Debug Counters - */ - protected static final String PACKAGE = TopologyManager.class.getPackage().getName(); - protected IDebugCounter ctrIncoming; - - // Getter/Setter methods - /** - * Get the time interval for the period topology updates, if any. - * The time returned is in milliseconds. - * @return - */ - public int getTopologyComputeInterval() { - return TOPOLOGY_COMPUTE_INTERVAL_MS; - } - - /** - * Set the time interval for the period topology updates, if any. - * The time is in milliseconds. - * @return - */ - public void setTopologyComputeInterval(int time_ms) { - TOPOLOGY_COMPUTE_INTERVAL_MS = time_ms; - } - - /** - * Thread for recomputing topology. The thread is always running, - * however the function applyUpdates() has a blocking call. - */ - protected class UpdateTopologyWorker implements Runnable { - @Override - public void run() { - try { - if (ldUpdates.peek() != null) { - updateTopology(); - } - handleMiscellaneousPeriodicEvents(); - } - catch (Exception e) { - log.error("Error in topology instance task thread", e); - } finally { - if (floodlightProviderService.getRole() != HARole.STANDBY) { - newInstanceTask.reschedule(TOPOLOGY_COMPUTE_INTERVAL_MS, TimeUnit.MILLISECONDS); - } - } - } - } - - // To be used for adding any periodic events that's required by topology. - protected void handleMiscellaneousPeriodicEvents() { - return; - } - - public boolean updateTopology() { - boolean newInstanceFlag; - linksUpdated = false; - dtLinksUpdated = false; - tunnelPortsUpdated = false; - List<LDUpdate> appliedUpdates = applyUpdates(); - newInstanceFlag = createNewInstance("link-discovery-updates"); - lastUpdateTime = new Date(); - informListeners(appliedUpdates); - return newInstanceFlag; - } - - // ********************** - // ILinkDiscoveryListener - // ********************** - - @Override - public void linkDiscoveryUpdate(List<LDUpdate> updateList) { - if (log.isTraceEnabled()) { - log.trace("Queuing update: {}", updateList); - } - ldUpdates.addAll(updateList); - } - - // **************** - // ITopologyService - // **************** - - @Override - public Map<DatapathId, Set<Link>> getAllLinks() { - - Map<DatapathId, Set<Link>> dpidLinks = new HashMap<DatapathId, Set<Link>>(); - TopologyInstance ti = getCurrentInstance(); - Set<DatapathId> switches = ti.getSwitches(); - - for(DatapathId s: switches) { - if (this.switchPorts.get(s) == null) continue; - for (OFPort p: switchPorts.get(s)) { - NodePortTuple np = new NodePortTuple(s, p); - if (this.switchPortLinks.get(np) == null) continue; - for(Link l: this.switchPortLinks.get(np)) { - if(dpidLinks.containsKey(s)) { - dpidLinks.get(s).add(l); - } - else { - dpidLinks.put(s,new HashSet<Link>(Arrays.asList(l))); - } - - } - } - } +public class TopologyManager implements IFloodlightModule, ITopologyService, + ITopologyManagerBackend, ILinkDiscoveryListener, IOFMessageListener { + + protected static Logger log = LoggerFactory.getLogger(TopologyManager.class); + public static final String MODULE_NAME = "topology"; + + protected static IStatisticsService statisticsService; + + protected static volatile PATH_METRIC pathMetric = PATH_METRIC.HOPCOUNT_AVOID_TUNNELS; //default: compute paths on hop count + protected static boolean collectStatistics = false; + + /** + * Maximum number of route entries stored in memory. + */ + private static volatile int maxPathsToCompute = 3; + + /** + * Role of the controller. + */ + private HARole role; + + /** + * Set of ports for each switch + */ + protected Map<DatapathId, Set<OFPort>> switchPorts; + + /** + * Set of links organized by node port tuple + */ + protected Map<NodePortTuple, Set<Link>> switchPortLinks; + + /** + * Set of direct links + */ + protected Map<NodePortTuple, Set<Link>> directLinks; + + /** + * set of links that are broadcast domain links. + */ + protected Map<NodePortTuple, Set<Link>> interClusterLinks; + + /** + * set of tunnel links + */ + protected Set<NodePortTuple> tunnelPorts; + + protected ILinkDiscoveryService linkDiscoveryService; + protected IThreadPoolService threadPoolService; + protected IFloodlightProviderService floodlightProviderService; + protected IOFSwitchService switchService; + protected IRestApiService restApiService; + protected IDebugCounterService debugCounterService; + + // Modules that listen to our updates + protected ArrayList<ITopologyListener> topologyAware; + + protected BlockingQueue<LDUpdate> ldUpdates; + + // These must be accessed using getCurrentInstance(), not directly + protected TopologyInstance currentInstance; + + protected SingletonTask newInstanceTask; + private Date lastUpdateTime; + + /** + * Flag that indicates if links (direct/tunnel/multihop links) were + * updated as part of LDUpdate. + */ + protected boolean linksUpdated; + /** + * Flag that indicates if direct or tunnel links were updated as + * part of LDUpdate. + */ + protected boolean dtLinksUpdated; + + /** Flag that indicates if tunnel ports were updated or not + */ + protected boolean tunnelPortsUpdated; + + protected int TOPOLOGY_COMPUTE_INTERVAL_MS = 500; + + private IHAListener haListener; + + /** + * Debug Counters + */ + protected static final String PACKAGE = TopologyManager.class.getPackage().getName(); + protected IDebugCounter ctrIncoming; + + // Getter/Setter methods + /** + * Get the time interval for the period topology updates, if any. + * The time returned is in milliseconds. + * @return + */ + public int getTopologyComputeInterval() { + return TOPOLOGY_COMPUTE_INTERVAL_MS; + } + + /** + * Set the time interval for the period topology updates, if any. + * The time is in milliseconds. + * @return + */ + public void setTopologyComputeInterval(int time_ms) { + TOPOLOGY_COMPUTE_INTERVAL_MS = time_ms; + } + + /** + * Thread for recomputing topology. The thread is always running, + * however the function applyUpdates() has a blocking call. + */ + protected class UpdateTopologyWorker implements Runnable { + @Override + public void run() { + try { + if (ldUpdates.peek() != null) { + updateTopology(); + } + handleMiscellaneousPeriodicEvents(); + } + catch (Exception e) { + log.error("Error in topology instance task thread", e); + } finally { + if (floodlightProviderService.getRole() != HARole.STANDBY) { + newInstanceTask.reschedule(TOPOLOGY_COMPUTE_INTERVAL_MS, TimeUnit.MILLISECONDS); + } + } + } + } + + // To be used for adding any periodic events that's required by topology. + protected void handleMiscellaneousPeriodicEvents() { + return; + } + + public boolean updateTopology() { + boolean newInstanceFlag; + linksUpdated = false; + dtLinksUpdated = false; + tunnelPortsUpdated = false; + List<LDUpdate> appliedUpdates = applyUpdates(); + newInstanceFlag = createNewInstance("link-discovery-updates"); + lastUpdateTime = new Date(); + informListeners(appliedUpdates); + return newInstanceFlag; + } + + // ********************** + // ILinkDiscoveryListener + // ********************** + + @Override + public void linkDiscoveryUpdate(List<LDUpdate> updateList) { + if (log.isTraceEnabled()) { + log.trace("Queuing update: {}", updateList); + } + ldUpdates.addAll(updateList); + } + + // **************** + // ITopologyService + // **************** + + @Override + public Map<DatapathId, Set<Link>> getAllLinks() { + + Map<DatapathId, Set<Link>> dpidLinks = new HashMap<DatapathId, Set<Link>>(); + TopologyInstance ti = getCurrentInstance(); + Set<DatapathId> switches = ti.getSwitches(); + + for(DatapathId s: switches) { + if (this.switchPorts.get(s) == null) continue; + for (OFPort p: switchPorts.get(s)) { + NodePortTuple np = new NodePortTuple(s, p); + if (this.switchPortLinks.get(np) == null) continue; + for(Link l: this.switchPortLinks.get(np)) { + if(dpidLinks.containsKey(s)) { + dpidLinks.get(s).add(l); + } + else { + dpidLinks.put(s,new HashSet<Link>(Arrays.asList(l))); + } + + } + } + } + + return dpidLinks; + } + + @Override + public boolean isEdge(DatapathId sw, OFPort p){ + TopologyInstance ti = getCurrentInstance(); + return ti.isEdge(sw, p); + } + + @Override + public Set<OFPort> getSwitchBroadcastPorts(DatapathId sw){ + TopologyInstance ti = getCurrentInstance(); + return ti.swBroadcastPorts(sw); + } + + @Override + public Date getLastUpdateTime() { + return lastUpdateTime; + } - return dpidLinks; - } - - @Override - public boolean isEdge(DatapathId sw, OFPort p){ - TopologyInstance ti = getCurrentInstance(); - return ti.isEdge(sw, p); - } - - @Override - public Set<OFPort> getSwitchBroadcastPorts(DatapathId sw){ - TopologyInstance ti = getCurrentInstance(); - return ti.swBroadcastPorts(sw); - } - - @Override - public Date getLastUpdateTime() { - return lastUpdateTime; - } - - @Override - public void addListener(ITopologyListener listener) { - topologyAware.add(listener); - } - - @Override + @Override + public void addListener(ITopologyListener listener) { + topologyAware.add(listener); + } + + @Override public void removeListener(ITopologyListener listener) { topologyAware.remove(listener); } - @Override - public boolean isAttachmentPointPort(DatapathId switchid, OFPort port) { - - // If the switch port is 'tun-bsn' port, it is not - // an attachment point port, irrespective of whether - // a link is found through it or not. - if (linkDiscoveryService.isTunnelPort(switchid, port)) - return false; - - TopologyInstance ti = getCurrentInstance(); - - // if the port is not attachment point port according to - // topology instance, then return false - if (ti.isAttachmentPointPort(switchid, port) == false) - return false; - - // Check whether the port is a physical port. We should not learn - // attachment points on "special" ports. - if ((port.getShortPortNumber() & 0xff00) == 0xff00 && port.getShortPortNumber() != (short)0xfffe) return false; - - // Make sure that the port is enabled. - IOFSwitch sw = switchService.getActiveSwitch(switchid); - if (sw == null) return false; - return (sw.portEnabled(port)); - } - - @Override - public DatapathId getClusterId(DatapathId switchId) { - TopologyInstance ti = getCurrentInstance(); - return ti.getClusterId(switchId); - } - - @Override - public boolean isInSameCluster(DatapathId switch1, DatapathId switch2) { - TopologyInstance ti = getCurrentInstance(); - return ti.isInSameCluster(switch1, switch2); - } - - @Override - public boolean isNotBlocked(DatapathId sw, OFPort port) { - TopologyInstance ti = getCurrentInstance(); - return !ti.isBlockedPort(new NodePortTuple(sw, port)); - } - - @Override - public PATH_METRIC setPathMetric(PATH_METRIC metric) { - pathMetric = metric; - return pathMetric; - } - - @Override - public PATH_METRIC getPathMetric() { - return pathMetric; - } - - protected static PATH_METRIC getPathMetricInternal() { - return pathMetric; - } - - protected static int getMaxPathsToComputeInternal() { - return maxPathsToCompute; - } - - @Override - public int getMaxPathsToCompute() { - return maxPathsToCompute; - } - - @Override - public void setMaxPathsToCompute(int max) { - maxPathsToCompute = max; - } - - //////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////// - - @Override - public boolean isBroadcastAllowed(DatapathId sw, OFPort portId) { - TopologyInstance ti = getCurrentInstance(); - return ti.isBroadcastAllowedOnSwitchPort(sw, portId); - } - - //////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////// - - @Override - public Set<OFPort> getPortsWithLinks(DatapathId sw) { - TopologyInstance ti = getCurrentInstance(); - return ti.getPortsWithLinks(sw); - } - - //////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////// - - /** - * Get all the ports on the target switch (targetSw) on which a - * broadcast packet must be sent from a host whose attachment point - * is on switch port (src, srcPort). - */ - @Override - public Set<OFPort> getBroadcastPorts(DatapathId targetSw, - DatapathId src, OFPort srcPort) { - TopologyInstance ti = getCurrentInstance(); - return ti.getBroadcastPorts(targetSw, src, srcPort); - } - - //////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////// - - @Override - public boolean isInSameArchipelago(DatapathId s1, DatapathId s2) { - TopologyInstance ti = getCurrentInstance(); - return ti.isInSameArchipelago(s1, s2); - - } - - //////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////// - - @Override - public boolean isBroadcastPort(DatapathId sw, OFPort port) { - TopologyInstance ti = getCurrentInstance(); - return ti.isBroadcastPort(new NodePortTuple(sw, port)); - } - - //////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////// - - @Override - public boolean isConsistent(DatapathId oldSw, OFPort oldPort, - DatapathId newSw, OFPort newPort) { - TopologyInstance ti = getCurrentInstance(); - return ti.isConsistent(oldSw, oldPort, newSw, newPort); - } - - //////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////// - - @Override - public Set<DatapathId> getSwitchesInCluster(DatapathId switchDPID) { - TopologyInstance ti = getCurrentInstance(); - return ti.getSwitchesInCluster(switchDPID); - } - //////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////// - - @Override - public Set<Link> getExternalInterClusterLinks() { - ImmutableSet.Builder<Link> b = ImmutableSet.builder(); + @Override + public boolean isAttachmentPointPort(DatapathId switchid, OFPort port) { + + // If the switch port is 'tun-bsn' port, it is not + // an attachment point port, irrespective of whether + // a link is found through it or not. + if (linkDiscoveryService.isTunnelPort(switchid, port)) + return false; + + TopologyInstance ti = getCurrentInstance(); + + // if the port is not attachment point port according to + // topology instance, then return false + if (ti.isAttachmentPointPort(switchid, port) == false) + return false; + + // Check whether the port is a physical port. We should not learn + // attachment points on "special" ports. + if ((port.getShortPortNumber() & 0xff00) == 0xff00 && port.getShortPortNumber() != (short)0xfffe) return false; + + // Make sure that the port is enabled. + IOFSwitch sw = switchService.getActiveSwitch(switchid); + if (sw == null) return false; + return (sw.portEnabled(port)); + } + + @Override + public DatapathId getClusterId(DatapathId switchId) { + TopologyInstance ti = getCurrentInstance(); + return ti.getClusterId(switchId); + } + + @Override + public boolean isInSameCluster(DatapathId switch1, DatapathId switch2) { + TopologyInstance ti = getCurrentInstance(); + return ti.isInSameCluster(switch1, switch2); + } + + @Override + public boolean isNotBlocked(DatapathId sw, OFPort port) { + TopologyInstance ti = getCurrentInstance(); + return !ti.isBlockedPort(new NodePortTuple(sw, port)); + } + + @Override + public void setPathMetric(PATH_METRIC metric) { + pathMetric = metric; + } + + @Override + public PATH_METRIC getPathMetric() { + return pathMetric; + } + + protected static PATH_METRIC getPathMetricInternal() { + return pathMetric; + } + + protected static int getMaxPathsToComputeInternal() { + return maxPathsToCompute; + } + + @Override + public int getMaxPathsToCompute() { + return maxPathsToCompute; + } + + @Override + public void setMaxPathsToCompute(int max) { + maxPathsToCompute = max; + } + + //////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////// + + @Override + public boolean isBroadcastAllowed(DatapathId sw, OFPort portId) { + TopologyInstance ti = getCurrentInstance(); + return ti.isBroadcastAllowedOnSwitchPort(sw, portId); + } + + //////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////// + + @Override + public Set<OFPort> getPortsWithLinks(DatapathId sw) { + TopologyInstance ti = getCurrentInstance(); + return ti.getPortsWithLinks(sw); + } + + //////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////// + + /** + * Get all the ports on the target switch (targetSw) on which a + * broadcast packet must be sent from a host whose attachment point + * is on switch port (src, srcPort). + */ + @Override + public Set<OFPort> getBroadcastPorts(DatapathId targetSw, + DatapathId src, OFPort srcPort) { + TopologyInstance ti = getCurrentInstance(); + return ti.getBroadcastPorts(targetSw, src, srcPort); + } + + //////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////// + + @Override + public boolean isInSameArchipelago(DatapathId s1, DatapathId s2) { + TopologyInstance ti = getCurrentInstance(); + return ti.isInSameArchipelago(s1, s2); + + } + + //////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////// + + @Override + public boolean isBroadcastPort(DatapathId sw, OFPort port) { + TopologyInstance ti = getCurrentInstance(); + return ti.isBroadcastPort(new NodePortTuple(sw, port)); + } + + //////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////// + + @Override + public boolean isConsistent(DatapathId oldSw, OFPort oldPort, + DatapathId newSw, OFPort newPort) { + TopologyInstance ti = getCurrentInstance(); + return ti.isConsistent(oldSw, oldPort, newSw, newPort); + } + + //////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////// + + @Override + public Set<DatapathId> getSwitchesInCluster(DatapathId switchDPID) { + TopologyInstance ti = getCurrentInstance(); + return ti.getSwitchesInCluster(switchDPID); + } + //////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////// + + @Override + public Set<Link> getExternalInterClusterLinks() { + ImmutableSet.Builder<Link> b = ImmutableSet.builder(); for (Collection<Link> c : interClusterLinks.values()) { for (Link l : c) { b.add(l); } } return b.build(); - } - - @Override + } + + @Override public Set<Link> getInternalInterClusterLinks() { TopologyInstance ti = getCurrentInstance(); return ti.getInternalInterClusterLinks(); } - @Override - public Set<NodePortTuple> getTunnelPorts() { - return tunnelPorts; - } - - @Override - public Set<NodePortTuple> getBlockedPorts() { - Set<NodePortTuple> bp; - Set<NodePortTuple> blockedPorts = - new HashSet<NodePortTuple>(); - - bp = getCurrentInstance().getBlockedPorts(); - if (bp != null) - blockedPorts.addAll(bp); - - return blockedPorts; - } - //////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////// - - // *************** - // IRoutingService - // *************** - - @Override - public Route getPath(DatapathId src, DatapathId dst) { - TopologyInstance ti = getCurrentInstance(); - return ti.getPath(src, dst); - } - - @Override - public Route getPath(DatapathId src, OFPort srcPort, DatapathId dst, OFPort dstPort) { - TopologyInstance ti = getCurrentInstance(); - return ti.getPath(src, srcPort, dst, dstPort); - } - - @Override - public boolean pathExists(DatapathId src, DatapathId dst) { - TopologyInstance ti = getCurrentInstance(); - return ti.pathExists(src, dst); - } - - @Override - public List<Route> getPathsFast(DatapathId srcDpid, DatapathId dstDpid) { - return getCurrentInstance().getPathsFast(srcDpid, dstDpid, maxPathsToCompute); - } - - @Override - public List<Route> getPathsFast(DatapathId srcDpid, DatapathId dstDpid, int k) { - return getCurrentInstance().getPathsFast(srcDpid, dstDpid, k); - } - - @Override - public List<Route> getPathsSlow(DatapathId srcDpid, DatapathId dstDpid, int k) { - return getCurrentInstance().getPathsSlow(srcDpid, dstDpid, k); - } - - public Map<Link, Integer> getLinkCostMap() { - TopologyInstance ti = getCurrentInstance(); - return ti.initLinkCostMap(); - } - - // ****************** - // IOFMessageListener - // ****************** - - @Override - public String getName() { - return MODULE_NAME; - } - - @Override - public boolean isCallbackOrderingPrereq(OFType type, String name) { - return "linkdiscovery".equals(name); - } - - @Override - public boolean isCallbackOrderingPostreq(OFType type, String name) { - return false; - } - - @Override - public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) { - switch (msg.getType()) { - case PACKET_IN: - ctrIncoming.increment(); - return this.processPacketInMessage(sw, (OFPacketIn) msg, cntx); - default: - break; - } + @Override + public Set<NodePortTuple> getTunnelPorts() { + return tunnelPorts; + } - return Command.CONTINUE; - } - - // *************** - // IHAListener - // *************** - - private class HAListenerDelegate implements IHAListener { - @Override - public void transitionToActive() { - role = HARole.ACTIVE; - log.debug("Re-computing topology due " + - "to HA change from STANDBY->ACTIVE"); - newInstanceTask.reschedule(TOPOLOGY_COMPUTE_INTERVAL_MS, - TimeUnit.MILLISECONDS); - } + @Override + public Set<NodePortTuple> getBlockedPorts() { + Set<NodePortTuple> bp; + Set<NodePortTuple> blockedPorts = + new HashSet<NodePortTuple>(); - @Override - public void controllerNodeIPsChanged( - Map<String, String> curControllerNodeIPs, - Map<String, String> addedControllerNodeIPs, - Map<String, String> removedControllerNodeIPs) { - } + bp = getCurrentInstance().getBlockedPorts(); + if (bp != null) + blockedPorts.addAll(bp); - @Override - public String getName() { - return TopologyManager.this.getName(); - } + return blockedPorts; + } + //////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////// - @Override - public boolean isCallbackOrderingPrereq(HAListenerTypeMarker type, - String name) { - return "linkdiscovery".equals(name); - } + public Map<Link, Integer> getLinkCostMap() { + TopologyInstance ti = getCurrentInstance(); + return ti.initLinkCostMap(); + } - @Override - public boolean isCallbackOrderingPostreq(HAListenerTypeMarker type, - String name) { - return false; - } + // ****************** + // IOFMessageListener + // ****************** - @Override - public void transitionToStandby() { } - } - - // ***************** - // IFloodlightModule - // ***************** - - @Override - public Collection<Class<? extends IFloodlightService>> getModuleServices() { - Collection<Class<? extends IFloodlightService>> l = - new ArrayList<Class<? extends IFloodlightService>>(); - l.add(ITopologyService.class); - l.add(IRoutingService.class); - return l; - } - - @Override - public Map<Class<? extends IFloodlightService>, IFloodlightService> - getServiceImpls() { - Map<Class<? extends IFloodlightService>, - IFloodlightService> m = - new HashMap<Class<? extends IFloodlightService>, - IFloodlightService>(); - // We are the class that implements the service - m.put(ITopologyService.class, this); - m.put(IRoutingService.class, this); - return m; - } - - @Override - public Collection<Class<? extends IFloodlightService>> - getModuleDependencies() { - Collection<Class<? extends IFloodlightService>> l = - new ArrayList<Class<? extends IFloodlightService>>(); - l.add(ILinkDiscoveryService.class); - l.add(IThreadPoolService.class); - l.add(IFloodlightProviderService.class); - l.add(IOFSwitchService.class); - l.add(IDebugCounterService.class); - l.add(IRestApiService.class); - return l; - } - - @Override - public void init(FloodlightModuleContext context) - throws FloodlightModuleException { - linkDiscoveryService = context.getServiceImpl(ILinkDiscoveryService.class); - threadPoolService = context.getServiceImpl(IThreadPoolService.class); - floodlightProviderService = context.getServiceImpl(IFloodlightProviderService.class); - switchService = context.getServiceImpl(IOFSwitchService.class); - restApiService = context.getServiceImpl(IRestApiService.class); - debugCounterService = context.getServiceImpl(IDebugCounterService.class); - statisticsService = context.getServiceImpl(IStatisticsService.class); - - switchPorts = new HashMap<DatapathId, Set<OFPort>>(); - switchPortLinks = new HashMap<NodePortTuple, Set<Link>>(); - directLinks = new HashMap<NodePortTuple, Set<Link>>(); - interClusterLinks = new HashMap<NodePortTuple, Set<Link>>(); - tunnelPorts = new HashSet<NodePortTuple>(); - topologyAware = new ArrayList<ITopologyListener>(); - ldUpdates = new LinkedBlockingQueue<LDUpdate>(); - haListener = new HAListenerDelegate(); - registerTopologyDebugCounters(); - - Map<String, String> configOptions = context.getConfigParams(this); - String metric = configOptions.get("pathMetric") != null - ? configOptions.get("pathMetric").trim().toLowerCase() : null; - if (metric != null) { - metric = metric.toLowerCase().trim(); - switch (metric) { - case "latency": - pathMetric = PATH_METRIC.LATENCY; - break; - case "utilization": - pathMetric = PATH_METRIC.UTILIZATION; - break; - case "hopcount": - pathMetric = PATH_METRIC.HOPCOUNT; - break; - case "hopcount_avoid_tunnels": - pathMetric = PATH_METRIC.HOPCOUNT_AVOID_TUNNELS; - break; - case "link_speed": - pathMetric = PATH_METRIC.LINK_SPEED; - break; - default: - log.error("Invalid routing metric {}. Using default {}", metric, pathMetric.getMetricName()); - break; - } - } - log.info("Path metrics set to {}", pathMetric); - - String maxroutes = configOptions.get("maxPathsToCompute") != null - ? configOptions.get("maxPathsToCompute").trim() : null; - if (maxroutes != null) { - try { - maxPathsToCompute = Integer.parseInt(maxroutes); - } catch (NumberFormatException e) { - log.error("Invalid 'maxPathsToCompute'. Using default {}", maxPathsToCompute); - } - } - log.info("Will compute a max of {} paths upon topology updates", maxPathsToCompute); - } + @Override + public String getName() { + return MODULE_NAME; + } - @Override - public void startUp(FloodlightModuleContext context) { - clearCurrentTopology(); - // Initialize role to floodlight provider role. - this.role = floodlightProviderService.getRole(); + @Override + public boolean isCallbackOrderingPrereq(OFType type, String name) { + return "linkdiscovery".equals(name); + } - ScheduledExecutorService ses = threadPoolService.getScheduledExecutor(); - newInstanceTask = new SingletonTask(ses, new UpdateTopologyWorker()); + @Override + public boolean isCallbackOrderingPostreq(OFType type, String name) { + return false; + } - if (role != HARole.STANDBY) { - newInstanceTask.reschedule(TOPOLOGY_COMPUTE_INTERVAL_MS, TimeUnit.MILLISECONDS); - } + @Override + public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) { + switch (msg.getType()) { + case PACKET_IN: + ctrIncoming.increment(); + return this.processPacketInMessage(sw, (OFPacketIn) msg, cntx); + default: + break; + } - linkDiscoveryService.addListener(this); - floodlightProviderService.addOFMessageListener(OFType.PACKET_IN, this); - floodlightProviderService.addHAListener(this.haListener); - addRestletRoutable(); - } + return Command.CONTINUE; + } - private void registerTopologyDebugCounters() throws FloodlightModuleException { - if (debugCounterService == null) { - log.error("debugCounterService should not be null. Has IDebugEventService been loaded previously?"); - } - debugCounterService.registerModule(PACKAGE); - ctrIncoming = debugCounterService.registerCounter( - PACKAGE, "incoming", - "All incoming packets seen by this module"); - } - - protected void addRestletRoutable() { - restApiService.addRestletRoutable(new TopologyWebRoutable()); - restApiService.addRestletRoutable(new RoutingWebRoutable()); - } - - // **************** - // Internal methods - // **************** - /** - * If the packet-in switch port is disabled for all data traffic, then - * the packet will be dropped. Otherwise, the packet will follow the - * normal processing chain. - * @param sw - * @param pi - * @param cntx - * @return - */ - protected Command dropFilter(DatapathId sw, OFPacketIn pi, - FloodlightContext cntx) { - Command result = Command.CONTINUE; - OFPort inPort = (pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT)); - - // If the input port is not allowed for data traffic, drop everything. - // BDDP packets will not reach this stage. - if (isNotBlocked(sw, inPort) == false) { - if (log.isTraceEnabled()) { - log.trace("Ignoring packet because of topology " + - "restriction on switch={}, port={}", sw.getLong(), inPort.getPortNumber()); - result = Command.STOP; - } - } - return result; - } - - /** - * Send a packet-out to multiple ports - * @param packetData - * @param sw - * @param ports - * @param cntx - */ - public void doMultiActionPacketOut(byte[] packetData, IOFSwitch sw, - Set<OFPort> ports, - FloodlightContext cntx) { - - if (ports == null) return; - if (packetData == null || packetData.length <= 0) return; - - //OFPacketOut po = (OFPacketOut) floodlightProvider.getOFMessageFactory().getMessage(OFType.PACKET_OUT); - OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut(); - List<OFAction> actions = new ArrayList<OFAction>(); - for(OFPort p: ports) { - //actions.add(new OFActionOutput(p, (short) 0)); - actions.add(sw.getOFFactory().actions().output(p, 0)); - } + // *************** + // IHAListener + // *************** + + private class HAListenerDelegate implements IHAListener { + @Override + public void transitionToActive() { + role = HARole.ACTIVE; + log.debug("Re-computing topology due " + + "to HA change from STANDBY->ACTIVE"); + newInstanceTask.reschedule(TOPOLOGY_COMPUTE_INTERVAL_MS, + TimeUnit.MILLISECONDS); + } + + @Override + public void controllerNodeIPsChanged( + Map<String, String> curControllerNodeIPs, + Map<String, String> addedControllerNodeIPs, + Map<String, String> removedControllerNodeIPs) { + } + + @Override + public String getName() { + return TopologyManager.this.getName(); + } - // set actions - pob.setActions(actions); - // set action length - //po.setActionsLength((short) (OFActionOutput.MINIMUM_LENGTH * ports.size())); - // set buffer-id to BUFFER_ID_NONE - pob.setBufferId(OFBufferId.NO_BUFFER); - // set in-port to OFPP_NONE - pob.setInPort(OFPort.ZERO); + @Override + public boolean isCallbackOrderingPrereq(HAListenerTypeMarker type, + String name) { + return "linkdiscovery".equals(name); + } - // set packet data - pob.setData(packetData); + @Override + public boolean isCallbackOrderingPostreq(HAListenerTypeMarker type, + String name) { + return false; + } - // compute and set packet length. - //short poLength = (short)(OFPacketOut.MINIMUM_LENGTH + po.getActionsLength() + packetData.length); + @Override + public void transitionToStandby() { } + } - //po.setLength(poLength); + // ***************** + // IFloodlightModule + // ***************** - //ctrIncoming.updatePktOutFMCounterStore(sw, po); - if (log.isTraceEnabled()) { - log.trace("write broadcast packet on switch-id={} " + - "interaces={} packet-data={} packet-out={}", - new Object[] {sw.getId(), ports, packetData, pob.build()}); - } - sw.write(pob.build(), LogicalOFMessageCategory.MAIN); - } - - /** - * Get the set of ports to eliminate for sending out BDDP. The method - * returns all the ports that are suppressed for link discovery on the - * switch. - * packets. - * @param sid - * @return - */ - protected Set<OFPort> getPortsToEliminateForBDDP(DatapathId sid) { - Set<NodePortTuple> suppressedNptList = linkDiscoveryService.getSuppressLLDPsInfo(); - if (suppressedNptList == null) return null; - - Set<OFPort> resultPorts = new HashSet<OFPort>(); - for(NodePortTuple npt: suppressedNptList) { - if (npt.getNodeId() == sid) { - resultPorts.add(npt.getPortId()); - } - } + @Override + public Collection<Class<? extends IFloodlightService>> getModuleServices() { + Collection<Class<? extends IFloodlightService>> l = + new ArrayList<Class<? extends IFloodlightService>>(); + l.add(ITopologyService.class); + l.add(IRoutingService.class); + return l; + } - return resultPorts; - } - - /** - * The BDDP packets are forwarded out of all the ports out of an - * openflowdomain. Get all the switches in the same openflow - * domain as the sw (disabling tunnels). Then get all the - * external switch ports and send these packets out. - * @param sw - * @param pi - * @param cntx - */ - protected void doFloodBDDP(DatapathId pinSwitch, OFPacketIn pi, - FloodlightContext cntx) { - - TopologyInstance ti = getCurrentInstance(); - - Set<DatapathId> switches = ti.getSwitchesInCluster(pinSwitch); - - if (switches == null) - { - // indicates no links are connected to the switches - switches = new HashSet<DatapathId>(); - switches.add(pinSwitch); - } + @Override + public Map<Class<? extends IFloodlightService>, IFloodlightService> + getServiceImpls() { + Map<Class<? extends IFloodlightService>, + IFloodlightService> m = + new HashMap<Class<? extends IFloodlightService>, + IFloodlightService>(); + // We are the class that implements the service + m.put(ITopologyService.class, this); + m.put(IRoutingService.class, this); + return m; + } - for (DatapathId sid : switches) { - IOFSwitch sw = switchService.getSwitch(sid); - if (sw == null) continue; - Collection<OFPort> enabledPorts = sw.getEnabledPortNumbers(); - if (enabledPorts == null) - continue; - Set<OFPort> ports = new HashSet<OFPort>(); - ports.addAll(enabledPorts); - - // all the ports known to topology // without tunnels. - // out of these, we need to choose only those that are - // broadcast port, otherwise, we should eliminate. - Set<OFPort> portsKnownToTopo = ti.getPortsWithLinks(sid); - - if (portsKnownToTopo != null) { - for (OFPort p : portsKnownToTopo) { - NodePortTuple npt = - new NodePortTuple(sid, p); - if (ti.isBroadcastPort(npt) == false) { - ports.remove(p); - } - } - } - - Set<OFPort> portsToEliminate = getPortsToEliminateForBDDP(sid); - if (portsToEliminate != null) { - ports.removeAll(portsToEliminate); - } - - // remove the incoming switch port - if (pinSwitch == sid) { - ports.remove((pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT))); - } - - // we have all the switch ports to which we need to broadcast. - doMultiActionPacketOut(pi.getData(), sw, ports, cntx); - } + @Override + public Collection<Class<? extends IFloodlightService>> + getModuleDependencies() { + Collection<Class<? extends IFloodlightService>> l = + new ArrayList<Class<? extends IFloodlightService>>(); + l.add(ILinkDiscoveryService.class); + l.add(IThreadPoolService.class); + l.add(IFloodlightProviderService.class); + l.add(IOFSwitchService.class); + l.add(IDebugCounterService.class); + l.add(IRestApiService.class); + return l; + } - } + @Override + public void init(FloodlightModuleContext context) + throws FloodlightModuleException { + linkDiscoveryService = context.getServiceImpl(ILinkDiscoveryService.class); + threadPoolService = context.getServiceImpl(IThreadPoolService.class); + floodlightProviderService = context.getServiceImpl(IFloodlightProviderService.class); + switchService = context.getServiceImpl(IOFSwitchService.class); + restApiService = context.getServiceImpl(IRestApiService.class); + debugCounterService = context.getServiceImpl(IDebugCounterService.class); + statisticsService = context.getServiceImpl(IStatisticsService.class); + + switchPorts = new HashMap<DatapathId, Set<OFPort>>(); + switchPortLinks = new HashMap<NodePortTuple, Set<Link>>(); + directLinks = new HashMap<NodePortTuple, Set<Link>>(); + interClusterLinks = new HashMap<NodePortTuple, Set<Link>>(); + tunnelPorts = new HashSet<NodePortTuple>(); + topologyAware = new ArrayList<ITopologyListener>(); + ldUpdates = new LinkedBlockingQueue<LDUpdate>(); + haListener = new HAListenerDelegate(); + registerTopologyDebugCounters(); + + Map<String, String> configOptions = context.getConfigParams(this); + String metric = configOptions.get("pathMetric") != null + ? configOptions.get("pathMetric").trim().toLowerCase() : null; + if (metric != null) { + metric = metric.toLowerCase().trim(); + switch (metric) { + case "latency": + pathMetric = PATH_METRIC.LATENCY; + break; + case "utilization": + pathMetric = PATH_METRIC.UTILIZATION; + break; + case "hopcount": + pathMetric = PATH_METRIC.HOPCOUNT; + break; + case "hopcount_avoid_tunnels": + pathMetric = PATH_METRIC.HOPCOUNT_AVOID_TUNNELS; + break; + case "link_speed": + pathMetric = PATH_METRIC.LINK_SPEED; + break; + default: + log.error("Invalid routing metric {}. Using default {}", metric, pathMetric.getMetricName()); + break; + } + } + log.info("Path metrics set to {}", pathMetric); + + String maxroutes = configOptions.get("maxPathsToCompute") != null + ? configOptions.get("maxPathsToCompute").trim() : null; + if (maxroutes != null) { + try { + maxPathsToCompute = Integer.parseInt(maxroutes); + } catch (NumberFormatException e) { + log.error("Invalid 'maxPathsToCompute'. Using default {}", maxPathsToCompute); + } + } + log.info("Will compute a max of {} paths upon topology updates", maxPathsToCompute); + } - protected Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx) { - // get the packet-in switch. - Ethernet eth = - IFloodlightProviderService.bcStore. - get(cntx,IFloodlightProviderService.CONTEXT_PI_PAYLOAD); + @Override + public void startUp(FloodlightModuleContext context) { + clearCurrentTopology(); + // Initialize role to floodlight provider role. + this.role = floodlightProviderService.getRole(); - if (eth.getPayload() instanceof BSN) { - BSN bsn = (BSN) eth.getPayload(); - if (bsn == null) return Command.STOP; - if (bsn.getPayload() == null) return Command.STOP; + ScheduledExecutorService ses = threadPoolService.getScheduledExecutor(); + newInstanceTask = new SingletonTask(ses, new UpdateTopologyWorker()); - // It could be a packet other than BSN LLDP, therefore - // continue with the regular processing. - if (bsn.getPayload() instanceof LLDP == false) - return Command.CONTINUE; + if (role != HARole.STANDBY) { + newInstanceTask.reschedule(TOPOLOGY_COMPUTE_INTERVAL_MS, TimeUnit.MILLISECONDS); + } - doFloodBDDP(sw.getId(), pi, cntx); - return Command.STOP; - } else { - return dropFilter(sw.getId(), pi, cntx); - } - } - - /** - * Updates concerning switch disconnect and port down are not processed. - * LinkDiscoveryManager is expected to process those messages and send - * multiple link removed messages. However, all the updates from - * LinkDiscoveryManager would be propagated to the listeners of topology. - */ - public List<LDUpdate> applyUpdates() { - List<LDUpdate> appliedUpdates = new ArrayList<LDUpdate>(); - LDUpdate update = null; - while (ldUpdates.peek() != null) { - try { - update = ldUpdates.take(); - } catch (Exception e) { - log.error("Error reading link discovery update.", e); - } - if (log.isDebugEnabled()) { - log.debug("Applying update: {}", update); - } - - switch (update.getOperation()) { - case LINK_UPDATED: - addOrUpdateLink(update.getSrc(), update.getSrcPort(), - update.getDst(), update.getDstPort(), - update.getLatency(), update.getType()); - break; - case LINK_REMOVED: - removeLink(update.getSrc(), update.getSrcPort(), - update.getDst(), update.getDstPort()); - break; - case SWITCH_UPDATED: - addOrUpdateSwitch(update.getSrc()); - break; - case SWITCH_REMOVED: - removeSwitch(update.getSrc()); - break; - case TUNNEL_PORT_ADDED: - addTunnelPort(update.getSrc(), update.getSrcPort()); - break; - case TUNNEL_PORT_REMOVED: - removeTunnelPort(update.getSrc(), update.getSrcPort()); - break; - case PORT_UP: case PORT_DOWN: - break; - } - // Add to the list of applied updates. - appliedUpdates.add(update); - } - return (Collections.unmodifiableList(appliedUpdates)); - } + linkDiscoveryService.addListener(this); + floodlightProviderService.addOFMessageListener(OFType.PACKET_IN, this); + floodlightProviderService.addHAListener(this.haListener); + addRestletRoutable(); + } + + private void registerTopologyDebugCounters() throws FloodlightModuleException { + if (debugCounterService == null) { + log.error("debugCounterService should not be null. Has IDebugEventService been loaded previously?"); + } + debugCounterService.registerModule(PACKAGE); + ctrIncoming = debugCounterService.registerCounter( + PACKAGE, "incoming", + "All incoming packets seen by this module"); + } - protected void addOrUpdateSwitch(DatapathId sw) { - /*TODO react appropriately + protected void addRestletRoutable() { + restApiService.addRestletRoutable(new TopologyWebRoutable()); + restApiService.addRestletRoutable(new RoutingWebRoutable()); + } + + // **************** + // Internal methods + // **************** + /** + * If the packet-in switch port is disabled for all data traffic, then + * the packet will be dropped. Otherwise, the packet will follow the + * normal processing chain. + * @param sw + * @param pi + * @param cntx + * @return + */ + protected Command dropFilter(DatapathId sw, OFPacketIn pi, + FloodlightContext cntx) { + Command result = Command.CONTINUE; + OFPort inPort = (pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT)); + + // If the input port is not allowed for data traffic, drop everything. + // BDDP packets will not reach this stage. + if (isNotBlocked(sw, inPort) == false) { + if (log.isTraceEnabled()) { + log.trace("Ignoring packet because of topology " + + "restriction on switch={}, port={}", sw.getLong(), inPort.getPortNumber()); + result = Command.STOP; + } + } + return result; + } + + /** + * Send a packet-out to multiple ports + * @param packetData + * @param sw + * @param ports + * @param cntx + */ + public void doMultiActionPacketOut(byte[] packetData, IOFSwitch sw, + Set<OFPort> ports, + FloodlightContext cntx) { + + if (ports == null) return; + if (packetData == null || packetData.length <= 0) return; + + //OFPacketOut po = (OFPacketOut) floodlightProvider.getOFMessageFactory().getMessage(OFType.PACKET_OUT); + OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut(); + List<OFAction> actions = new ArrayList<OFAction>(); + for(OFPort p: ports) { + //actions.add(new OFActionOutput(p, (short) 0)); + actions.add(sw.getOFFactory().actions().output(p, 0)); + } + + // set actions + pob.setActions(actions); + // set action length + //po.setActionsLength((short) (OFActionOutput.MINIMUM_LENGTH * ports.size())); + // set buffer-id to BUFFER_ID_NONE + pob.setBufferId(OFBufferId.NO_BUFFER); + // set in-port to OFPP_NONE + pob.setInPort(OFPort.ZERO); + + // set packet data + pob.setData(packetData); + + // compute and set packet length. + //short poLength = (short)(OFPacketOut.MINIMUM_LENGTH + po.getActionsLength() + packetData.length); + + //po.setLength(poLength); + + //ctrIncoming.updatePktOutFMCounterStore(sw, po); + if (log.isTraceEnabled()) { + log.trace("write broadcast packet on switch-id={} " + + "interaces={} packet-data={} packet-out={}", + new Object[] {sw.getId(), ports, packetData, pob.build()}); + } + sw.write(pob.build(), LogicalOFMessageCategory.MAIN); + } + + /** + * Get the set of ports to eliminate for sending out BDDP. The method + * returns all the ports that are suppressed for link discovery on the + * switch. + * packets. + * @param sid + * @return + */ + protected Set<OFPort> getPortsToEliminateForBDDP(DatapathId sid) { + Set<NodePortTuple> suppressedNptList = linkDiscoveryService.getSuppressLLDPsInfo(); + if (suppressedNptList == null) return null; + + Set<OFPort> resultPorts = new HashSet<OFPort>(); + for(NodePortTuple npt: suppressedNptList) { + if (npt.getNodeId() == sid) { + resultPorts.add(npt.getPortId()); + } + } + + return resultPorts; + } + + /** + * The BDDP packets are forwarded out of all the ports out of an + * openflowdomain. Get all the switches in the same openflow + * domain as the sw (disabling tunnels). Then get all the + * external switch ports and send these packets out. + * @param sw + * @param pi + * @param cntx + */ + protected void doFloodBDDP(DatapathId pinSwitch, OFPacketIn pi, + FloodlightContext cntx) { + + TopologyInstance ti = getCurrentInstance(); + + Set<DatapathId> switches = ti.getSwitchesInCluster(pinSwitch); + + if (switches == null) + { + // indicates no links are connected to the switches + switches = new HashSet<DatapathId>(); + switches.add(pinSwitch); + } + + for (DatapathId sid : switches) { + IOFSwitch sw = switchService.getSwitch(sid); + if (sw == null) continue; + Collection<OFPort> enabledPorts = sw.getEnabledPortNumbers(); + if (enabledPorts == null) + continue; + Set<OFPort> ports = new HashSet<OFPort>(); + ports.addAll(enabledPorts); + + // all the ports known to topology // without tunnels. + // out of these, we need to choose only those that are + // broadcast port, otherwise, we should eliminate. + Set<OFPort> portsKnownToTopo = ti.getPortsWithLinks(sid); + + if (portsKnownToTopo != null) { + for (OFPort p : portsKnownToTopo) { + NodePortTuple npt = + new NodePortTuple(sid, p); + if (ti.isBroadcastPort(npt) == false) { + ports.remove(p); + } + } + } + + Set<OFPort> portsToEliminate = getPortsToEliminateForBDDP(sid); + if (portsToEliminate != null) { + ports.removeAll(portsToEliminate); + } + + // remove the incoming switch port + if (pinSwitch == sid) { + ports.remove((pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT))); + } + + // we have all the switch ports to which we need to broadcast. + doMultiActionPacketOut(pi.getData(), sw, ports, cntx); + } + + } + + protected Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx) { + // get the packet-in switch. + Ethernet eth = + IFloodlightProviderService.bcStore. + get(cntx,IFloodlightProviderService.CONTEXT_PI_PAYLOAD); + + if (eth.getPayload() instanceof BSN) { + BSN bsn = (BSN) eth.getPayload(); + if (bsn == null) return Command.STOP; + if (bsn.getPayload() == null) return Command.STOP; + + // It could be a packet other than BSN LLDP, therefore + // continue with the regular processing. + if (bsn.getPayload() instanceof LLDP == false) + return Command.CONTINUE; + + doFloodBDDP(sw.getId(), pi, cntx); + return Command.STOP; + } else { + return dropFilter(sw.getId(), pi, cntx); + } + } + + /** + * Updates concerning switch disconnect and port down are not processed. + * LinkDiscoveryManager is expected to process those messages and send + * multiple link removed messages. However, all the updates from + * LinkDiscoveryManager would be propagated to the listeners of topology. + */ + public List<LDUpdate> applyUpdates() { + List<LDUpdate> appliedUpdates = new ArrayList<LDUpdate>(); + LDUpdate update = null; + while (ldUpdates.peek() != null) { + try { + update = ldUpdates.take(); + } catch (Exception e) { + log.error("Error reading link discovery update.", e); + } + if (log.isDebugEnabled()) { + log.debug("Applying update: {}", update); + } + + switch (update.getOperation()) { + case LINK_UPDATED: + addOrUpdateLink(update.getSrc(), update.getSrcPort(), + update.getDst(), update.getDstPort(), + update.getLatency(), update.getType()); + break; + case LINK_REMOVED: + removeLink(update.getSrc(), update.getSrcPort(), + update.getDst(), update.getDstPort()); + break; + case SWITCH_UPDATED: + addOrUpdateSwitch(update.getSrc()); + break; + case SWITCH_REMOVED: + removeSwitch(update.getSrc()); + break; + case TUNNEL_PORT_ADDED: + addTunnelPort(update.getSrc(), update.getSrcPort()); + break; + case TUNNEL_PORT_REMOVED: + removeTunnelPort(update.getSrc(), update.getSrcPort()); + break; + case PORT_UP: case PORT_DOWN: + break; + } + // Add to the list of applied updates. + appliedUpdates.add(update); + } + return (Collections.unmodifiableList(appliedUpdates)); + } + + protected void addOrUpdateSwitch(DatapathId sw) { + /*TODO react appropriately addSwitch(sw); for (OFPortDesc p : switchService.getSwitch(sw).getPorts()) { addPortToSwitch(sw, p.getPortNo()); } - */ - return; - } - - public void addTunnelPort(DatapathId sw, OFPort port) { - NodePortTuple npt = new NodePortTuple(sw, port); - tunnelPorts.add(npt); - tunnelPortsUpdated = true; - } - - public void removeTunnelPort(DatapathId sw, OFPort port) { - NodePortTuple npt = new NodePortTuple(sw, port); - tunnelPorts.remove(npt); - tunnelPortsUpdated = true; - } - - public boolean createNewInstance() { - return createNewInstance("internal"); - } - - /** - * This function computes a new topology instance. - * It ignores links connected to all broadcast domain ports - * and tunnel ports. The method returns if a new instance of - * topology was created or not. - */ - protected boolean createNewInstance(String reason) { - Set<NodePortTuple> blockedPorts = new HashSet<NodePortTuple>(); - - if (!linksUpdated) return false; - - Map<NodePortTuple, Set<Link>> openflowLinks; - openflowLinks = - new HashMap<NodePortTuple, Set<Link>>(); - Set<NodePortTuple> nptList = switchPortLinks.keySet(); - - if (nptList != null) { - for(NodePortTuple npt: nptList) { - Set<Link> linkSet = switchPortLinks.get(npt); - if (linkSet == null) continue; - openflowLinks.put(npt, new HashSet<Link>(linkSet)); - } - } + */ + return; + } - // Identify all broadcast domain ports. - // Mark any port that has inconsistent set of links - // as broadcast domain ports as well. - Set<NodePortTuple> broadcastDomainPorts = - identifyBroadcastDomainPorts(); - - // Remove all links incident on broadcast domain ports. - for (NodePortTuple npt : broadcastDomainPorts) { - if (switchPortLinks.get(npt) == null) continue; - for (Link link : switchPortLinks.get(npt)) { - removeLinkFromStructure(openflowLinks, link); - } - } + public void addTunnelPort(DatapathId sw, OFPort port) { + NodePortTuple npt = new NodePortTuple(sw, port); + tunnelPorts.add(npt); + tunnelPortsUpdated = true; + } - // Remove all tunnel links. - for (NodePortTuple npt: tunnelPorts) { - if (switchPortLinks.get(npt) == null) continue; - for (Link link : switchPortLinks.get(npt)) { - removeLinkFromStructure(openflowLinks, link); - } - } - //switchPorts contains only ports that are part of links. Calculation of broadcast ports needs set of all ports. - Map<DatapathId, Set<OFPort>> allPorts = new HashMap<DatapathId, Set<OFPort>>();; - for (DatapathId sw : switchPorts.keySet()){ - allPorts.put(sw, this.getPorts(sw)); - } + public void removeTunnelPort(DatapathId sw, OFPort port) { + NodePortTuple npt = new NodePortTuple(sw, port); + tunnelPorts.remove(npt); + tunnelPortsUpdated = true; + } - TopologyInstance nt = new TopologyInstance(switchPorts, - blockedPorts, - openflowLinks, - broadcastDomainPorts, - tunnelPorts, - switchPortLinks, - allPorts, - interClusterLinks); - - nt.compute(); - - currentInstance = nt; - - return true; - } - - /** - * We expect every switch port to have at most two links. Both these - * links must be unidirectional links connecting to the same switch port. - * If not, we will mark this as a broadcast domain port. - */ - protected Set<NodePortTuple> identifyBroadcastDomainPorts() { - - Set<NodePortTuple> broadcastDomainPorts = - new HashSet<NodePortTuple>(); - broadcastDomainPorts.addAll(this.interClusterLinks.keySet()); - - Set<NodePortTuple> additionalNpt = - new HashSet<NodePortTuple>(); - - // Copy switchPortLinks - Map<NodePortTuple, Set<Link>> spLinks = - new HashMap<NodePortTuple, Set<Link>>(); - for (NodePortTuple npt : switchPortLinks.keySet()) { - spLinks.put(npt, new HashSet<Link>(switchPortLinks.get(npt))); - } + public boolean createNewInstance() { + return createNewInstance("internal"); + } - for (NodePortTuple npt : spLinks.keySet()) { - Set<Link> links = spLinks.get(npt); - boolean bdPort = false; - ArrayList<Link> linkArray = new ArrayList<Link>(); - if (links.size() > 2) { - bdPort = true; - } else if (links.size() == 2) { - for (Link l : links) { - linkArray.add(l); - } - // now, there should be two links in [0] and [1]. - Link l1 = linkArray.get(0); - Link l2 = linkArray.get(1); - - // check if these two are symmetric. - if (!l1.getSrc().equals(l2.getDst()) || - !l1.getSrcPort().equals(l2.getDstPort()) || - !l1.getDst().equals(l2.getSrc()) || - !l1.getDstPort().equals(l2.getSrcPort())) { - bdPort = true; - } - } - - if (bdPort && (broadcastDomainPorts.contains(npt) == false)) { - additionalNpt.add(npt); - } - } + /** + * This function computes a new topology instance. + * It ignores links connected to all broadcast domain ports + * and tunnel ports. The method returns if a new instance of + * topology was created or not. + */ + protected boolean createNewInstance(String reason) { + Set<NodePortTuple> blockedPorts = new HashSet<NodePortTuple>(); - if (additionalNpt.size() > 0) { - log.warn("The following switch ports have multiple " + - "links incident on them, so these ports will be treated " + - " as braodcast domain ports. {}", additionalNpt); + if (!linksUpdated) return false; - broadcastDomainPorts.addAll(additionalNpt); - } - return broadcastDomainPorts; - } + Map<NodePortTuple, Set<Link>> openflowLinks; + openflowLinks = + new HashMap<NodePortTuple, Set<Link>>(); + Set<NodePortTuple> nptList = switchPortLinks.keySet(); + if (nptList != null) { + for(NodePortTuple npt: nptList) { + Set<Link> linkSet = switchPortLinks.get(npt); + if (linkSet == null) continue; + openflowLinks.put(npt, new HashSet<Link>(linkSet)); + } + } + // Identify all broadcast domain ports. + // Mark any port that has inconsistent set of links + // as broadcast domain ports as well. + Set<NodePortTuple> broadcastDomainPorts = + identifyBroadcastDomainPorts(); + + // Remove all links incident on broadcast domain ports. + for (NodePortTuple npt : broadcastDomainPorts) { + if (switchPortLinks.get(npt) == null) continue; + for (Link link : switchPortLinks.get(npt)) { + removeLinkFromStructure(openflowLinks, link); + } + } + + // Remove all tunnel links. + for (NodePortTuple npt: tunnelPorts) { + if (switchPortLinks.get(npt) == null) continue; + for (Link link : switchPortLinks.get(npt)) { + removeLinkFromStructure(openflowLinks, link); + } + } + //switchPorts contains only ports that are part of links. Calculation of broadcast ports needs set of all ports. + Map<DatapathId, Set<OFPort>> allPorts = new HashMap<DatapathId, Set<OFPort>>();; + for (DatapathId sw : switchPorts.keySet()){ + allPorts.put(sw, this.getPorts(sw)); + } - public void informListeners(List<LDUpdate> linkUpdates) { + TopologyInstance nt = new TopologyInstance(switchPorts, + blockedPorts, + openflowLinks, + broadcastDomainPorts, + tunnelPorts, + switchPortLinks, + allPorts, + interClusterLinks); - if (role != null && role != HARole.ACTIVE) - return; + nt.compute(); - for(int i=0; i < topologyAware.size(); ++i) { - ITopologyListener listener = topologyAware.get(i); - listener.topologyChanged(linkUpdates); - } - } + currentInstance = nt; - public void addSwitch(DatapathId sid) { - if (switchPorts.containsKey(sid) == false) { - switchPorts.put(sid, new HashSet<OFPort>()); - } - } - - private void addPortToSwitch(DatapathId s, OFPort p) { - addSwitch(s); - switchPorts.get(s).add(p); - } - - public void removeSwitch(DatapathId sid) { - // Delete all the links in the switch, switch and all - // associated data should be deleted. - if (switchPorts.containsKey(sid) == false) return; - - // Check if any tunnel ports need to be removed. - for(NodePortTuple npt: tunnelPorts) { - if (npt.getNodeId() == sid) { - removeTunnelPort(npt.getNodeId(), npt.getPortId()); - } - } + return true; + } - Set<Link> linksToRemove = new HashSet<Link>(); - for(OFPort p: switchPorts.get(sid)) { - NodePortTuple n1 = new NodePortTuple(sid, p); - linksToRemove.addAll(switchPortLinks.get(n1)); - } + /** + * We expect every switch port to have at most two links. Both these + * links must be unidirectional links connecting to the same switch port. + * If not, we will mark this as a broadcast domain port. + */ + protected Set<NodePortTuple> identifyBroadcastDomainPorts() { - if (linksToRemove.isEmpty()) return; + Set<NodePortTuple> broadcastDomainPorts = + new HashSet<NodePortTuple>(); + broadcastDomainPorts.addAll(this.interClusterLinks.keySet()); - for(Link link: linksToRemove) { - removeLink(link); - } - } - - /** - * Add the given link to the data structure. - * @param s - * @param l - */ - private void addLinkToStructure(Map<NodePortTuple, Set<Link>> s, Link l) { - NodePortTuple n1 = new NodePortTuple(l.getSrc(), l.getSrcPort()); - NodePortTuple n2 = new NodePortTuple(l.getDst(), l.getDstPort()); - - if (s.get(n1) == null) { - s.put(n1, new HashSet<Link>()); - } - if (s.get(n2) == null) { - s.put(n2, new HashSet<Link>()); - } + Set<NodePortTuple> additionalNpt = + new HashSet<NodePortTuple>(); - /* - * Since we don't include latency in .equals(), we need - * to explicitly remove the existing link (if present). - * Otherwise, new latency values for existing links will - * never be accepted. - */ + // Copy switchPortLinks + Map<NodePortTuple, Set<Link>> spLinks = + new HashMap<NodePortTuple, Set<Link>>(); + for (NodePortTuple npt : switchPortLinks.keySet()) { + spLinks.put(npt, new HashSet<Link>(switchPortLinks.get(npt))); + } + + for (NodePortTuple npt : spLinks.keySet()) { + Set<Link> links = spLinks.get(npt); + boolean bdPort = false; + ArrayList<Link> linkArray = new ArrayList<Link>(); + if (links.size() > 2) { + bdPort = true; + } else if (links.size() == 2) { + for (Link l : links) { + linkArray.add(l); + } + // now, there should be two links in [0] and [1]. + Link l1 = linkArray.get(0); + Link l2 = linkArray.get(1); + + // check if these two are symmetric. + if (!l1.getSrc().equals(l2.getDst()) || + !l1.getSrcPort().equals(l2.getDstPort()) || + !l1.getDst().equals(l2.getSrc()) || + !l1.getDstPort().equals(l2.getSrcPort())) { + bdPort = true; + } + } + + if (bdPort && (broadcastDomainPorts.contains(npt) == false)) { + additionalNpt.add(npt); + } + } + + if (additionalNpt.size() > 0) { + log.warn("The following switch ports have multiple " + + "links incident on them, so these ports will be treated " + + " as braodcast domain ports. {}", additionalNpt); + + broadcastDomainPorts.addAll(additionalNpt); + } + return broadcastDomainPorts; + } + + + + public void informListeners(List<LDUpdate> linkUpdates) { + + if (role != null && role != HARole.ACTIVE) + return; + + for(int i=0; i < topologyAware.size(); ++i) { + ITopologyListener listener = topologyAware.get(i); + listener.topologyChanged(linkUpdates); + } + } + + public void addSwitch(DatapathId sid) { + if (switchPorts.containsKey(sid) == false) { + switchPorts.put(sid, new HashSet<OFPort>()); + } + } + + private void addPortToSwitch(DatapathId s, OFPort p) { + addSwitch(s); + switchPorts.get(s).add(p); + } + + public void removeSwitch(DatapathId sid) { + // Delete all the links in the switch, switch and all + // associated data should be deleted. + if (switchPorts.containsKey(sid) == false) return; + + // Check if any tunnel ports need to be removed. + for(NodePortTuple npt: tunnelPorts) { + if (npt.getNodeId() == sid) { + removeTunnelPort(npt.getNodeId(), npt.getPortId()); + } + } + + Set<Link> linksToRemove = new HashSet<Link>(); + for(OFPort p: switchPorts.get(sid)) { + NodePortTuple n1 = new NodePortTuple(sid, p); + linksToRemove.addAll(switchPortLinks.get(n1)); + } + + if (linksToRemove.isEmpty()) return; + + for(Link link: linksToRemove) { + removeLink(link); + } + } + + /** + * Add the given link to the data structure. + * @param s + * @param l + */ + private void addLinkToStructure(Map<NodePortTuple, Set<Link>> s, Link l) { + NodePortTuple n1 = new NodePortTuple(l.getSrc(), l.getSrcPort()); + NodePortTuple n2 = new NodePortTuple(l.getDst(), l.getDstPort()); + + if (s.get(n1) == null) { + s.put(n1, new HashSet<Link>()); + } + if (s.get(n2) == null) { + s.put(n2, new HashSet<Link>()); + } + + /* + * Since we don't include latency in .equals(), we need + * to explicitly remove the existing link (if present). + * Otherwise, new latency values for existing links will + * never be accepted. + */ s.get(n1).remove(l); s.get(n2).remove(l); s.get(n1).add(l); @@ -1202,12 +1166,12 @@ public class TopologyManager implements IFloodlightModule, ITopologyService, IRo } protected void addOrUpdateTunnelLink(DatapathId srcId, OFPort srcPort, DatapathId dstId, - OFPort dstPort, U64 latency) { + OFPort dstPort, U64 latency) { // If you need to handle tunnel links, this is a placeholder. } public void addOrUpdateLink(DatapathId srcId, OFPort srcPort, DatapathId dstId, - OFPort dstPort, U64 latency, LinkType type) { + OFPort dstPort, U64 latency, LinkType type) { Link link = new Link(srcId, srcPort, dstId, dstPort, latency); if (type.equals(LinkType.MULTIHOP_LINK)) { @@ -1265,7 +1229,7 @@ public class TopologyManager implements IFloodlightModule, ITopologyService, IRo } public void removeLink(DatapathId srcId, OFPort srcPort, - DatapathId dstId, OFPort dstPort) { + DatapathId dstId, OFPort dstPort) { Link link = new Link(srcId, srcPort, dstId, dstPort, U64.ZERO /* does not matter for remove (not included in .equals() of Link) */); removeLink(link); } @@ -1354,4 +1318,9 @@ public class TopologyManager implements IFloodlightModule, ITopologyService, IRo TopologyInstance ti = getCurrentInstance(); return ti.getArchipelagoIds(); } + + @Override + public TopologyInstance getCurrentTopologyInstance() { + return getCurrentInstance(); + } } \ No newline at end of file diff --git a/src/test/java/net/floodlightcontroller/topology/TopologyInstanceTest.java b/src/test/java/net/floodlightcontroller/topology/TopologyInstanceTest.java index afe5df42bea3680014f0bf5545303d95159820b5..0d0986e8735e9ece98eab72fcd63a6aec41d9c5f 100644 --- a/src/test/java/net/floodlightcontroller/topology/TopologyInstanceTest.java +++ b/src/test/java/net/floodlightcontroller/topology/TopologyInstanceTest.java @@ -34,7 +34,9 @@ import net.floodlightcontroller.debugcounter.IDebugCounterService; import net.floodlightcontroller.debugcounter.MockDebugCounterService; import net.floodlightcontroller.linkdiscovery.ILinkDiscovery; import net.floodlightcontroller.linkdiscovery.ILinkDiscoveryService; +import net.floodlightcontroller.routing.IRoutingService; import net.floodlightcontroller.routing.Route; +import net.floodlightcontroller.routing.RoutingManager; import net.floodlightcontroller.threadpool.IThreadPoolService; import net.floodlightcontroller.core.types.NodePortTuple; import net.floodlightcontroller.topology.TopologyInstance; @@ -52,6 +54,7 @@ import org.slf4j.LoggerFactory; public class TopologyInstanceTest { protected static Logger log = LoggerFactory.getLogger(TopologyInstanceTest.class); protected TopologyManager topologyManager; + protected RoutingManager routingManager; protected FloodlightModuleContext fmc; protected ILinkDiscoveryService linkDiscovery; protected MockFloodlightProvider mockFloodlightProvider; @@ -71,8 +74,13 @@ public class TopologyInstanceTest { fmc.addService(IDebugCounterService.class, new MockDebugCounterService()); MockThreadPoolService tp = new MockThreadPoolService(); topologyManager = new TopologyManager(); + routingManager = new RoutingManager(); + fmc.addService(IRoutingService.class, routingManager); fmc.addService(IThreadPoolService.class, tp); + fmc.addService(ITopologyService.class, topologyManager); topologyManager.init(fmc); + routingManager.init(fmc); + routingManager.startUp(fmc); tp.init(fmc); tp.startUp(fmc); } @@ -691,7 +699,7 @@ public class TopologyInstanceTest { */ topologyManager.setPathMetric(LATENCY); configureTopology(linkArray, lat); - List<Route> lat_paths = topologyManager.getPathsFast(one, three, k); + List<Route> lat_paths = routingManager.getPathsFast(one, three, k); log.info("Path 1: {}", lat_paths.get(0)); log.info("Path 2: {}", lat_paths.get(1)); @@ -700,7 +708,7 @@ public class TopologyInstanceTest { topologyManager.setPathMetric(HOPCOUNT); configureTopology(linkArray, lat); topologyManager.createNewInstance(); - List<Route> hop_paths = topologyManager.getPathsFast(one, three, k); + List<Route> hop_paths = routingManager.getPathsFast(one, three, k); log.info("Path 1: {}", hop_paths.get(0)); log.info("Path 2: {}", hop_paths.get(1)); @@ -712,7 +720,7 @@ public class TopologyInstanceTest { int [] lat1 = {1,50,1}; configureTopology(linkArray, lat1); topologyManager.createNewInstance(); - List<Route> r1 = topologyManager.getPathsFast(one, three, k); + List<Route> r1 = routingManager.getPathsFast(one, three, k); assertTrue((r1.get(0)).equals(lat_paths.get(0))); assertTrue((r1.get(1)).equals(lat_paths.get(1))); @@ -776,7 +784,7 @@ public class TopologyInstanceTest { int [] lat4 = {3,2,4,2,1,1,2,3,2}; configureTopology(linkArray2, lat4); topologyManager.createNewInstance(); - List<Route> r = topologyManager.getPathsFast(one, six, k); + List<Route> r = routingManager.getPathsFast(one, six, k); for(int i = 0; i< r.size(); i++) { log.info("k = (1000) => Route: {}", r.get(i)); } @@ -792,7 +800,7 @@ public class TopologyInstanceTest { k = 7; configureTopology(linkArray2, lat4); topologyManager.createNewInstance(); - List<Route> r2 = topologyManager.getPathsFast(one, six, k); + List<Route> r2 = routingManager.getPathsFast(one, six, k); for(int i = 0; i< r2.size(); i++) { log.info("k = (7) => Route: {}", r2.get(i)); } @@ -808,7 +816,7 @@ public class TopologyInstanceTest { k = -1; configureTopology(linkArray2, lat4); topologyManager.createNewInstance(); - List<Route> r3 = topologyManager.getPathsFast(one, six, k); + List<Route> r3 = routingManager.getPathsFast(one, six, k); for(int i = 0; i< r3.size(); i++) { log.info("HOPCOUNT.k = (-1) => Route: {}", r3.get(i)); } @@ -824,7 +832,7 @@ public class TopologyInstanceTest { k = -1; configureTopology(linkArray2, lat4); topologyManager.createNewInstance(); - List<Route> r4 = topologyManager.getPathsFast(one, six, k); + List<Route> r4 = routingManager.getPathsFast(one, six, k); for(int i = 0; i< r4.size(); i++) { log.info("LATENCY.k = (-1) => Route: {}", r4.get(i)); } @@ -840,7 +848,7 @@ public class TopologyInstanceTest { k = 3; configureTopology(linkArray2, lat4); topologyManager.createNewInstance(); - List<Route> r5 = topologyManager.getPathsFast(one, six, k); + List<Route> r5 = routingManager.getPathsFast(one, six, k); for(int i = 0; i< r5.size(); i++) { log.info("HOPCOUNT.k = (3) => Route: {}", r5.get(i)); } @@ -856,7 +864,7 @@ public class TopologyInstanceTest { k = 4; configureTopology(linkArray2, lat4); topologyManager.createNewInstance(); - List<Route> r6 = topologyManager.getPathsFast(one, six, k); + List<Route> r6 = routingManager.getPathsFast(one, six, k); for(int i = 0; i< r6.size(); i++) { log.info("LATENCY.k = (4) => Route: {}", r6.get(i)); } @@ -873,7 +881,7 @@ public class TopologyInstanceTest { int [] lat5 = {0,0,0,0,0,0,0,0,0}; configureTopology(linkArray2, lat5); topologyManager.createNewInstance(); - List<Route> r7 = topologyManager.getPathsFast(one, six, k); + List<Route> r7 = routingManager.getPathsFast(one, six, k); for(int i = 0; i< r7.size(); i++) { log.info("Route latency all ZERO: {}", r7.get(i)); } @@ -888,7 +896,7 @@ public class TopologyInstanceTest { k = 4; configureTopology(linkArray2, lat4); topologyManager.createNewInstance(); - List<Route> r8 = topologyManager.getPathsFast(one, one, k); + List<Route> r8 = routingManager.getPathsFast(one, one, k); for(int i = 0; i< r8.size(); i++) { log.info("(src == dst) => Route: {}", r8.get(i)); } @@ -903,7 +911,7 @@ public class TopologyInstanceTest { k = 4; configureTopology(linkArray2, lat4); topologyManager.createNewInstance(); - List<Route> r9 = topologyManager.getPathsFast(six, one, k); + List<Route> r9 = routingManager.getPathsFast(six, one, k); for(int i = 0; i< r9.size(); i++) { log.info("Reversed Route (6 -> 1): {}", r9.get(i)); } @@ -919,7 +927,7 @@ public class TopologyInstanceTest { k = 4; configureTopology(linkArray2, lat4); topologyManager.createNewInstance(); - List<Route> r10 = topologyManager.getPathsFast(one, DatapathId.of(7), k); + List<Route> r10 = routingManager.getPathsFast(one, DatapathId.of(7), k); for(int i = 0; i< r10.size(); i++) { log.info("(src == 7) => Route: {}", r10.get(i)); }