From 58e60539d2e58b750d46b5834d2a7de814e3ea43 Mon Sep 17 00:00:00 2001 From: Ryan Izard <ryan.izard@bigswitch.com> Date: Fri, 15 Jul 2016 17:33:50 -0400 Subject: [PATCH] Create RoutingManager that is separate from TopologyManger. It hooks into TopologyManager via new ITopologyManagerBackend, butmakes it much more obvious that the TopologyManger and TopologyInstance is responsible for much of the routing. Before this change, the actual 'routing' and 'path-finding' implementation location wasn't very clear. --- .../routing/IRoutingService.java | 6 +- .../routing/RoutingManager.java | 110 + .../routing/web/PathMetricsResource.java | 8 +- .../topology/ITopologyManagerBackend.java | 13 + .../topology/TopologyInstance.java | 72 +- .../topology/TopologyManager.java | 2073 ++++++++--------- .../topology/TopologyInstanceTest.java | 34 +- 7 files changed, 1206 insertions(+), 1110 deletions(-) create mode 100644 src/main/java/net/floodlightcontroller/routing/RoutingManager.java create mode 100644 src/main/java/net/floodlightcontroller/topology/ITopologyManagerBackend.java diff --git a/src/main/java/net/floodlightcontroller/routing/IRoutingService.java b/src/main/java/net/floodlightcontroller/routing/IRoutingService.java index 76c8d56e6..be6c98e90 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 000000000..f5d8c42c6 --- /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 73fe3f754..cf032a135 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 000000000..64c4e06a2 --- /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 318ba2352..d3f50f404 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 f5ad1592d..d5b1729c8 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 afe5df42b..0d0986e87 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)); } -- GitLab