diff --git a/src/main/java/net/floodlightcontroller/core/IFloodlightProviderService.java b/src/main/java/net/floodlightcontroller/core/IFloodlightProviderService.java index a24f774cbe051d4a27020c9054aedd0ced94474f..be61662c5e190993b8b0a63efd79faa4df1a6413 100644 --- a/src/main/java/net/floodlightcontroller/core/IFloodlightProviderService.java +++ b/src/main/java/net/floodlightcontroller/core/IFloodlightProviderService.java @@ -68,6 +68,12 @@ public interface IFloodlightProviderService extends IFloodlightService { * @param listener The component that no longer wants to receive the message */ public void removeOFMessageListener(OFType type, IOFMessageListener listener); + + /** + * Return a non-modifiable list of all current listeners + * @return listeners + */ + public Map<OFType, List<IOFMessageListener>> getListeners(); /** * Returns a list of all actively connected OpenFlow switches. This doesn't @@ -81,6 +87,11 @@ public interface IFloodlightProviderService extends IFloodlightService { */ public Role getRole(); + /** + * Gets the ID of the controller + */ + public String getControllerId(); + /** * Set the role of the controller */ @@ -88,21 +99,27 @@ public interface IFloodlightProviderService extends IFloodlightService { /** * Add a switch listener - * @param listener + * @param listener The module that wants to listen for events */ public void addOFSwitchListener(IOFSwitchListener listener); /** * Remove a switch listener - * @param listener + * @param listener The The module that no longer wants to listen for events */ public void removeOFSwitchListener(IOFSwitchListener listener); - + /** - * Return a non-modifiable list of all current listeners - * @return listeners + * Adds a listener for HA role events + * @param listener The module that wants to listen for events */ - public Map<OFType, List<IOFMessageListener>> getListeners(); + public void addHAListener(IHARoleListener listener); + + /** + * Removes a listener for HA role events + * @param listener The module that no longer wants to listen for events + */ + public void removeHAListener(IHARoleListener listener); /** * Terminate the process diff --git a/src/main/java/net/floodlightcontroller/core/IHARoleListener.java b/src/main/java/net/floodlightcontroller/core/IHARoleListener.java new file mode 100644 index 0000000000000000000000000000000000000000..83749caed0d7e33fb2d177d4837d43b2d95f5d97 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/IHARoleListener.java @@ -0,0 +1,13 @@ +package net.floodlightcontroller.core; + +import net.floodlightcontroller.core.IFloodlightProviderService.Role; + +public interface IHARoleListener { + /** + * Gets called when the controller changes role (i.e. Master -> Slave). + * Note that oldRole CAN be null. + * @param oldRole The controller's old role + * @param newRole The controller's new role + */ + public void roleChanged(Role oldRole, Role newRole); +} diff --git a/src/main/java/net/floodlightcontroller/core/internal/Controller.java b/src/main/java/net/floodlightcontroller/core/internal/Controller.java index 590d0c353b34ee161f1e1b09248d088e57de6dc7..9876d2547078e6887d1597dce71f8e43fe1a8f27 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/Controller.java +++ b/src/main/java/net/floodlightcontroller/core/internal/Controller.java @@ -17,7 +17,6 @@ package net.floodlightcontroller.core.internal; -import java.io.FileInputStream; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; @@ -32,7 +31,6 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.Properties; import java.util.Set; import java.util.Stack; import java.util.concurrent.BlockingQueue; @@ -48,6 +46,7 @@ import java.util.concurrent.TimeoutException; import net.floodlightcontroller.core.FloodlightContext; import net.floodlightcontroller.core.IFloodlightProviderService; +import net.floodlightcontroller.core.IHARoleListener; import net.floodlightcontroller.core.IInfoProvider; import net.floodlightcontroller.core.IOFMessageListener; import net.floodlightcontroller.core.IOFMessageListener.Command; @@ -146,6 +145,7 @@ public class Controller implements IFloodlightProviderService { protected ConcurrentHashMap<Long, IOFSwitch> connectedSwitches; protected Set<IOFSwitchListener> switchListeners; + protected Set<IHARoleListener> haListeners; protected Map<String, List<IInfoProvider>> providerMap; protected BlockingQueue<Update> updates; @@ -159,6 +159,7 @@ public class Controller implements IFloodlightProviderService { // Configuration options protected int openFlowPort = 6633; protected int workerThreads = 0; + protected String controllerId = "localhost"; // The current role of the controller. // If the controller isn't configured to support roles, then this is null. @@ -199,13 +200,26 @@ public class Controller implements IFloodlightProviderService { protected static final int BATCH_MAX_SIZE = 100; protected static final boolean ALWAYS_DECODE_ETH = true; + protected enum UpdateType { + SWITCH, HA + } protected class Update { + public UpdateType type; public IOFSwitch sw; public boolean added; + public Role oldRole; + public Role newRole; public Update(IOFSwitch sw, boolean added) { this.sw = sw; this.added = added; + this.type = UpdateType.SWITCH; + } + + public Update(Role newRole, Role oldRole) { + this.oldRole = oldRole; + this.newRole = newRole; + this.type = UpdateType.HA; } } @@ -240,6 +254,8 @@ public class Controller implements IFloodlightProviderService { @Override public synchronized void setRole(Role role) { + if (role == null) throw new NullPointerException("Role can not be null."); + Role oldRole = this.role; this.role = role; // Send role request messages to all of the connected switches. @@ -256,8 +272,8 @@ public class Controller implements IFloodlightProviderService { } } - // Send an update - // TODO send update + // Enqueue an update for our listeners. + this.updates.add(new Update(role, oldRole)); } /** @@ -1368,8 +1384,9 @@ public class Controller implements IFloodlightProviderService { return factory; } + @Override public String getControllerId() { - return "localhost"; + return controllerId; } // ************** @@ -1521,25 +1538,13 @@ public class Controller implements IFloodlightProviderService { storageSource.deleteRowAsync(PORT_TABLE_NAME, id); } - protected Role getInitialRole() { - // FIXME: This code should be changed to get the settings from - // the property file used by the module loader once Alex has - // that code written instead of using system properties. + /** + * Sets the role based on a string. + * @param roleString The role string + * @return The role is a valid string is passed, nulll otherwise + */ + protected Role getInitialRole(String roleString) { Role role = null; - String roleString = System.getProperty("floodlight.role"); - if (roleString == null) { - String rolePath = System.getProperty("floodlight.role.path"); - if (rolePath != null) { - Properties properties = new Properties(); - try { - properties.load(new FileInputStream(rolePath)); - roleString = properties.getProperty("floodlight.role"); - } - catch (IOException exc) { - log.error("Error reading current role value from file: {}", rolePath); - } - } - } if (roleString != null) { // Canonicalize the string to the form used for the enum constants @@ -1552,6 +1557,7 @@ public class Controller implements IFloodlightProviderService { } } + log.info("Controller roles set to {}", role); return role; } @@ -1584,15 +1590,35 @@ public class Controller implements IFloodlightProviderService { while (true) { try { Update update = updates.take(); - log.debug("Dispatching switch update {} {}", - update.sw, update.added); - if (switchListeners != null) { - for (IOFSwitchListener listener : switchListeners) { - if (update.added) - listener.addedSwitch(update.sw); - else - listener.removedSwitch(update.sw); - } + switch (update.type) { + case SWITCH: + if (log.isDebugEnabled()) { + log.debug("Dispatching switch update {} {}", + update.sw, update.added); + } + if (switchListeners != null) { + for (IOFSwitchListener listener : switchListeners) { + if (update.added) + listener.addedSwitch(update.sw); + else + listener.removedSwitch(update.sw); + } + } + break; + case HA: + if (log.isDebugEnabled()) { + log.debug("Dispatching HA update newRole = {}, oldRole = {}", + update.newRole, update.oldRole); + } + if (haListeners != null) { + for (IHARoleListener listener : haListeners) { + listener.roleChanged(update.oldRole, update.newRole); + } + } + break; + default: + log.error("Unreognized update type " + update.type); + break; } } catch (InterruptedException e) { return; @@ -1602,7 +1628,8 @@ public class Controller implements IFloodlightProviderService { e, StackTraceUtil.stackTraceToString(e)); return; } catch (Exception e) { - log.error("Exception in controller updates loop {} {}", e, StackTraceUtil.stackTraceToString(e)); + log.error("Exception in controller updates loop {} {}", + e, StackTraceUtil.stackTraceToString(e)); } } } @@ -1632,6 +1659,11 @@ public class Controller implements IFloodlightProviderService { this.workerThreads = Integer.parseInt(threads); } log.info("Number of worker threads port set to {}", this.workerThreads); + String controllerId = configParams.get("controllerid"); + if (controllerId != null) { + this.controllerId = controllerId; + } + log.info("ControllerId set to {}", this.controllerId); } private void initVendorMessages() { @@ -1663,13 +1695,14 @@ public class Controller implements IFloodlightProviderService { ListenerDispatcher<OFType, IOFMessageListener>>(); this.switchListeners = new CopyOnWriteArraySet<IOFSwitchListener>(); + this.haListeners = new CopyOnWriteArraySet<IHARoleListener>(); this.activeSwitches = new ConcurrentHashMap<Long, IOFSwitch>(); this.connectedSwitches = new ConcurrentHashMap<Long, IOFSwitch>(); this.updates = new LinkedBlockingQueue<Update>(); this.factory = new BasicFactory(); this.providerMap = new HashMap<String, List<IInfoProvider>>(); setConfigParams(configParams); - this.setRole(getInitialRole()); + this.role = getInitialRole(configParams.get("role")); initVendorMessages(); } @@ -1735,4 +1768,14 @@ public class Controller implements IFloodlightProviderService { return result; } + + @Override + public void addHAListener(IHARoleListener listener) { + this.haListeners.add(listener); + } + + @Override + public void removeHAListener(IHARoleListener listener) { + this.haListeners.remove(listener); + } } diff --git a/src/main/java/net/floodlightcontroller/core/web/RoleResource.java b/src/main/java/net/floodlightcontroller/core/web/RoleResource.java index 45d170fbfbb0b0bbf1265d43a02da27b0d5de95c..d0bf2f837e62be1d4b915a1f23b60eb36e66d92d 100644 --- a/src/main/java/net/floodlightcontroller/core/web/RoleResource.java +++ b/src/main/java/net/floodlightcontroller/core/web/RoleResource.java @@ -61,6 +61,7 @@ public class RoleResource extends ServerResource { if (role == null) { log.warn ("Invalid role value specified in REST API to set controller role"); setStatus(Status.CLIENT_ERROR_BAD_REQUEST, "Invalid role value"); + return; } IFloodlightProviderService floodlightProvider = diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java index e37fc488d3a723ec890c84aa78a072440c5f2ea9..e8145f9762829a1d9fe692fdcd94617ddadc31bf 100644 --- a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java +++ b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java @@ -41,6 +41,8 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; import net.floodlightcontroller.core.FloodlightContext; import net.floodlightcontroller.core.IFloodlightProviderService; +import net.floodlightcontroller.core.IFloodlightProviderService.Role; +import net.floodlightcontroller.core.IHARoleListener; import net.floodlightcontroller.core.IInfoProvider; import net.floodlightcontroller.core.IOFMessageListener; import net.floodlightcontroller.core.IOFSwitch; @@ -120,7 +122,7 @@ import org.slf4j.LoggerFactory; public class LinkDiscoveryManager implements IOFMessageListener, IOFSwitchListener, IStorageSourceListener, ILinkDiscoveryService, - IFloodlightModule, IInfoProvider { + IFloodlightModule, IInfoProvider, IHARoleListener { protected static Logger log = LoggerFactory.getLogger(LinkDiscoveryManager.class); // Names of table/fields for links in the storage API @@ -184,11 +186,6 @@ public class LinkDiscoveryManager protected BlockingQueue<LDUpdate> updates; protected Thread updatesThread; - //This map provides the ids of broadcast domains connected to a switch cluster - protected Map<Long, Set<Long>> switchClusterBroadcastDomainMap; - - protected boolean isTopologyValid = false; - public int getLldpFrequency() { return lldpFrequency; } @@ -1418,6 +1415,7 @@ public class LinkDiscoveryManager floodlightProvider.addOFMessageListener(OFType.PORT_STATUS, this); // Register for switch updates floodlightProvider.addOFSwitchListener(this); + floodlightProvider.addHAListener(this); floodlightProvider.addInfoProvider("summary", this); // init our rest api @@ -1507,4 +1505,27 @@ public class LinkDiscoveryManager return info; } + + // IHARoleListener + + @Override + public void roleChanged(Role oldRole, Role newRole) { + switch(newRole) { + case MASTER: + if (oldRole == Role.SLAVE) { + log.debug("Sending LLDPs " + + "to HA change from SLAVE->MASTER"); + sendLLDPs(); + } + break; + case SLAVE: + log.debug("Clearing links due to " + + "HA change to SLAVE"); + switchLinks.clear(); + links.clear(); + portLinks.clear(); + portBroadcastDomainLinks.clear(); + break; + } + } } diff --git a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java index 2be3de223b03e8975968a355854b6b122a002ff9..bff3a422fa9795bb15efb2892b0c15612adb3848 100644 --- a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java +++ b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java @@ -15,6 +15,8 @@ import java.util.concurrent.ConcurrentHashMap; import net.floodlightcontroller.core.FloodlightContext; import net.floodlightcontroller.core.IFloodlightProviderService; +import net.floodlightcontroller.core.IFloodlightProviderService.Role; +import net.floodlightcontroller.core.IHARoleListener; import net.floodlightcontroller.core.IOFMessageListener; import net.floodlightcontroller.core.IOFSwitch; import net.floodlightcontroller.core.IOFSwitchListener; @@ -44,7 +46,7 @@ import org.slf4j.LoggerFactory; public class StaticFlowEntryPusher implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService, - IStorageSourceListener, IOFMessageListener { + IStorageSourceListener, IOFMessageListener, IHARoleListener { protected static Logger log = LoggerFactory.getLogger(StaticFlowEntryPusher.class); public static final String StaticFlowName = "staticflowentry"; @@ -544,8 +546,9 @@ public class StaticFlowEntryPusher public void startUp(FloodlightModuleContext context) { floodlightProvider.addOFMessageListener(OFType.FLOW_REMOVED, this); floodlightProvider.addOFSwitchListener(this); - // assumes no switches connected at startup() + floodlightProvider.addHAListener(this); + // assumes no switches connected at startup() storageSource.createTable(TABLE_NAME, null); storageSource.setTablePrimaryKeyName(TABLE_NAME, COLUMN_NAME); storageSource.addListener(TABLE_NAME, this); @@ -601,4 +604,27 @@ public class StaticFlowEntryPusher public Map<String, OFFlowMod> getFlows(String dpid) { return entriesFromStorage.get(dpid); } + + + // IHARoleListener + + @Override + public void roleChanged(Role oldRole, Role newRole) { + switch(newRole) { + case MASTER: + if (oldRole == Role.SLAVE) { + log.debug("Re-reading static flows from storage due " + + "to HA change from SLAVE->MASTER"); + entriesFromStorage = readEntriesFromStorage(); + entry2dpid = computeEntry2DpidMap(entriesFromStorage); + } + break; + case SLAVE: + log.debug("Clearing in-memory flows due to " + + "HA change to SLAVE"); + entry2dpid.clear(); + entriesFromStorage.clear(); + break; + } + } } diff --git a/src/main/java/net/floodlightcontroller/topology/ITopologyService.java b/src/main/java/net/floodlightcontroller/topology/ITopologyService.java index f2c95087d103ef44924e1a3f54acb01b9f9b4397..725c0d25ddba98a92bead784f5df7163970ec83e 100644 --- a/src/main/java/net/floodlightcontroller/topology/ITopologyService.java +++ b/src/main/java/net/floodlightcontroller/topology/ITopologyService.java @@ -15,6 +15,11 @@ public interface ITopologyService extends IFloodlightService { */ public boolean isInternal(long switchid, short port); + /** + * Returns the cluster ID of a given switch. + * @param switchId The DPID of the switch in long form + * @return The DPID of the switch that is the key for the cluster + */ public long getSwitchClusterId(long switchId); /** @@ -42,7 +47,7 @@ public interface ITopologyService extends IFloodlightService { * islands could be different than clusters. * @param switch1 * @param switch2 - * @return + * @return True of they are in the same island, false otherwise */ public boolean inSameIsland(long switch1, long switch2); @@ -81,6 +86,11 @@ public interface ITopologyService extends IFloodlightService { public boolean isInSameBroadcastDomain(long s1, short p1, long s2, short p2); + /** + * Gets a list of ports on a given switch + * @param sw The switch DPID in long + * @return The set of ports on this switch + */ public Set<Short> getPorts(long sw); public Set<Short> getBroadcastPorts(long targetSw, long src, short srcPort); diff --git a/src/main/java/net/floodlightcontroller/topology/TopologyManager.java b/src/main/java/net/floodlightcontroller/topology/TopologyManager.java index 2ec736eb04325ff44d5b2c89bd0f800097fd206e..6dd7037d5aebba46ad05dad5603cff132982fdfd 100644 --- a/src/main/java/net/floodlightcontroller/topology/TopologyManager.java +++ b/src/main/java/net/floodlightcontroller/topology/TopologyManager.java @@ -11,6 +11,9 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import net.floodlightcontroller.core.IFloodlightProviderService; +import net.floodlightcontroller.core.IFloodlightProviderService.Role; +import net.floodlightcontroller.core.IHARoleListener; import net.floodlightcontroller.core.module.FloodlightModuleContext; import net.floodlightcontroller.core.module.FloodlightModuleException; import net.floodlightcontroller.core.module.IFloodlightModule; @@ -36,8 +39,9 @@ import org.slf4j.LoggerFactory; * */ -public class TopologyManager implements IFloodlightModule, ITopologyService, -IRoutingService, ILinkDiscoveryListener { +public class TopologyManager + implements IFloodlightModule, ITopologyService, IRoutingService, + ILinkDiscoveryListener, IHARoleListener { protected static Logger log = LoggerFactory.getLogger(TopologyManager.class); @@ -45,9 +49,13 @@ IRoutingService, ILinkDiscoveryListener { protected Map<NodePortTuple, Set<Link>> switchPortLinks; // Set of links organized by node port tuple protected Map<NodePortTuple, Set<Link>> portBroadcastDomainLinks; // set of links that are broadcast domain links. protected Map<NodePortTuple, Set<Link>> tunnelLinks; // set of tunnel links + + // Dependencies protected ILinkDiscoveryService linkDiscovery; - protected ArrayList<ITopologyListener> topologyAware; protected IThreadPoolService threadPool; + protected IFloodlightProviderService floodlightProvider; + // Modules that listen to our updates + protected ArrayList<ITopologyListener> topologyAware; protected BlockingQueue<LDUpdate> ldUpdates; protected TopologyInstance currentInstance; @@ -318,6 +326,7 @@ IRoutingService, ILinkDiscoveryListener { new ArrayList<Class<? extends IFloodlightService>>(); l.add(ILinkDiscoveryService.class); l.add(IThreadPoolService.class); + l.add(IFloodlightProviderService.class); return l; } @@ -326,6 +335,7 @@ IRoutingService, ILinkDiscoveryListener { throws FloodlightModuleException { linkDiscovery = context.getServiceImpl(ILinkDiscoveryService.class); threadPool = context.getServiceImpl(IThreadPoolService.class); + floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class); switchPorts = new HashMap<Long,Set<Short>>(); switchPortLinks = new HashMap<NodePortTuple, Set<Link>>(); @@ -333,7 +343,6 @@ IRoutingService, ILinkDiscoveryListener { tunnelLinks = new HashMap<NodePortTuple, Set<Link>>(); topologyAware = new ArrayList<ITopologyListener>(); ldUpdates = new LinkedBlockingQueue<LDUpdate>(); - } @Override @@ -341,6 +350,7 @@ IRoutingService, ILinkDiscoveryListener { ScheduledExecutorService ses = threadPool.getScheduledExecutor(); newInstanceTask = new SingletonTask(ses, new NewInstanceWorker()); linkDiscovery.addListener(this); + floodlightProvider.addHAListener(this); newInstanceTask.reschedule(1, TimeUnit.MILLISECONDS); } @@ -459,5 +469,35 @@ IRoutingService, ILinkDiscoveryListener { public boolean inSameIsland(long switch1, long switch2) { return currentInstance.inSameIsland(switch1, switch2); } + + /** + * Clears the current topology. Note that this does NOT + * send out updates. + */ + public void clearCurrentTopology() { + switchPorts.clear(); + switchPortLinks.clear(); + portBroadcastDomainLinks.clear(); + tunnelLinks.clear(); + createNewInstance(); + } + + @Override + public void roleChanged(Role oldRole, Role newRole) { + switch(newRole) { + case MASTER: + if (oldRole == Role.SLAVE) { + log.debug("Re-computing topology due " + + "to HA change from SLAVE->MASTER"); + newInstanceTask.reschedule(1, TimeUnit.MILLISECONDS); + } + break; + case SLAVE: + log.debug("Clearing topology due to " + + "HA change to SLAVE"); + clearCurrentTopology(); + break; + } + } } diff --git a/src/main/java/net/floodlightcontroller/ui/web/StaticWebRoutable.java b/src/main/java/net/floodlightcontroller/ui/web/StaticWebRoutable.java index 145418baaa35d4594ae5627d7c89679ff8aadb69..c1d5b5f471ba09c2252a2abc24737488bbe0d1fb 100644 --- a/src/main/java/net/floodlightcontroller/ui/web/StaticWebRoutable.java +++ b/src/main/java/net/floodlightcontroller/ui/web/StaticWebRoutable.java @@ -50,7 +50,7 @@ public class StaticWebRoutable implements RestletRoutable, IFloodlightModule { @Override public void startUp(FloodlightModuleContext context) { // Add our REST API - restApi.addRestletRoutable(new StaticWebRoutable()); + restApi.addRestletRoutable(this); } diff --git a/src/test/java/net/floodlightcontroller/core/test/MockFloodlightProvider.java b/src/test/java/net/floodlightcontroller/core/test/MockFloodlightProvider.java index e903c5ea3f7e613c22648c7e005b416ab3bd27a8..56dc20fcba11f7448c5e8705cbad83c50ec77931 100644 --- a/src/test/java/net/floodlightcontroller/core/test/MockFloodlightProvider.java +++ b/src/test/java/net/floodlightcontroller/core/test/MockFloodlightProvider.java @@ -27,6 +27,7 @@ import java.util.concurrent.CopyOnWriteArrayList; import net.floodlightcontroller.core.FloodlightContext; import net.floodlightcontroller.core.IFloodlightProviderService; +import net.floodlightcontroller.core.IHARoleListener; import net.floodlightcontroller.core.IInfoProvider; import net.floodlightcontroller.core.IOFMessageListener; import net.floodlightcontroller.core.IOFSwitch; @@ -51,6 +52,7 @@ import org.openflow.protocol.factory.BasicFactory; public class MockFloodlightProvider implements IFloodlightModule, IFloodlightProviderService { protected Map<OFType, List<IOFMessageListener>> listeners; protected List<IOFSwitchListener> switchListeners; + protected List<IHARoleListener> haListeners; protected Map<Long, IOFSwitch> switches; protected BasicFactory factory; @@ -61,6 +63,7 @@ public class MockFloodlightProvider implements IFloodlightModule, IFloodlightPro listeners = new ConcurrentHashMap<OFType, List<IOFMessageListener>>(); switches = new ConcurrentHashMap<Long, IOFSwitch>(); switchListeners = new CopyOnWriteArrayList<IOFSwitchListener>(); + haListeners = new CopyOnWriteArrayList<IHARoleListener>(); factory = new BasicFactory(); } @@ -212,15 +215,6 @@ public class MockFloodlightProvider implements IFloodlightModule, IFloodlightPro // TODO Auto-generated method stub return null; } - - @Override - public Role getRole() { - return null; - } - - @Override - public void setRole(Role role) { - } @Override public @@ -254,4 +248,40 @@ public class MockFloodlightProvider implements IFloodlightModule, IFloodlightPro // TODO Auto-generated method stub return null; } + + @Override + public void addHAListener(IHARoleListener listener) { + haListeners.add(listener); + } + + @Override + public void removeHAListener(IHARoleListener listener) { + haListeners.remove(listener); + } + + @Override + public Role getRole() { + return null; + } + + @Override + public void setRole(Role role) { + + } + + /** + * Dispatches a new role change notification + * @param oldRole + * @param newRole + */ + public void dispatchRoleChanged(Role oldRole, Role newRole) { + for (IHARoleListener rl : haListeners) { + rl.roleChanged(oldRole, newRole); + } + } + + @Override + public String getControllerId() { + return "localhost"; + } } diff --git a/src/test/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManagerTest.java b/src/test/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManagerTest.java index c6521b93ae79aefc5d695483312033b8d2dda46d..fec23ee04999d32d44931fc4694bd2aa8bf2c1a3 100644 --- a/src/test/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManagerTest.java +++ b/src/test/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManagerTest.java @@ -29,6 +29,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import net.floodlightcontroller.core.IFloodlightProviderService; +import net.floodlightcontroller.core.IFloodlightProviderService.Role; import net.floodlightcontroller.core.IOFSwitch; import net.floodlightcontroller.core.module.FloodlightModuleContext; import net.floodlightcontroller.core.test.MockThreadPoolService; @@ -51,11 +52,25 @@ import net.floodlightcontroller.topology.TopologyManager; */ public class LinkDiscoveryManagerTest extends FloodlightTestCase { - private LinkDiscoveryManager topology; + private TestLinkDiscoveryManager ldm; protected static Logger log = LoggerFactory.getLogger(LinkDiscoveryManagerTest.class); + public class TestLinkDiscoveryManager extends LinkDiscoveryManager { + public boolean isSendLLDPsCalled = false; + + @Override + protected void sendLLDPs() { + isSendLLDPsCalled = true; + super.sendLLDPs(); + } + + public void reset() { + isSendLLDPsCalled = false; + } + } + public LinkDiscoveryManager getTopology() { - return topology; + return ldm; } public IOFSwitch createMockSwitch(Long id) { @@ -68,22 +83,22 @@ public class LinkDiscoveryManagerTest extends FloodlightTestCase { public void setUp() throws Exception { super.setUp(); FloodlightModuleContext cntx = new FloodlightModuleContext(); - topology = new LinkDiscoveryManager(); + ldm = new TestLinkDiscoveryManager(); TopologyManager routingEngine = new TopologyManager(); - topology.linkDiscoveryAware = new ArrayList<ILinkDiscoveryListener>(); + ldm.linkDiscoveryAware = new ArrayList<ILinkDiscoveryListener>(); MockThreadPoolService tp = new MockThreadPoolService(); cntx.addService(IThreadPoolService.class, tp); cntx.addService(IRoutingService.class, routingEngine); - cntx.addService(ILinkDiscoveryService.class, topology); - cntx.addService(ITopologyService.class, topology); + cntx.addService(ILinkDiscoveryService.class, ldm); + cntx.addService(ITopologyService.class, ldm); cntx.addService(IStorageSourceService.class, new MemoryStorageSource()); cntx.addService(IFloodlightProviderService.class, getMockFloodlightProvider()); tp.init(cntx); routingEngine.init(cntx); - topology.init(cntx); + ldm.init(cntx); tp.startUp(cntx); routingEngine.startUp(cntx); - topology.startUp(cntx); + ldm.startUp(cntx); } @Test @@ -216,8 +231,8 @@ public class LinkDiscoveryManagerTest extends FloodlightTestCase { LinkDiscoveryManager topology = getTopology(); IOFSwitch sw1 = createMockSwitch(1L); IOFSwitch sw2 = createMockSwitch(2L); - //expect(topology.getSwitchClusterId(1L)).andReturn(1L).anyTimes(); - //expect(topology.getSwitchClusterId(2L)).andReturn(1L).anyTimes(); + //expect(ldm.getSwitchClusterId(1L)).andReturn(1L).anyTimes(); + //expect(ldm.getSwitchClusterId(2L)).andReturn(1L).anyTimes(); replay(sw1, sw2); LinkTuple lt = new LinkTuple(sw1, 1, sw2, 1); LinkInfo info; @@ -329,4 +344,32 @@ public class LinkDiscoveryManagerTest extends FloodlightTestCase { assertTrue(topology.portBroadcastDomainLinks.get(lt.getDst()).contains(lt)); } + @Test + public void testHARoleChange() throws Exception { + LinkDiscoveryManager topology = getTopology(); + IOFSwitch sw1 = createMockSwitch(1L); + IOFSwitch sw2 = createMockSwitch(2L); + replay(sw1, sw2); + LinkTuple lt = new LinkTuple(sw1, 2, sw2, 1); + LinkInfo info = new LinkInfo(System.currentTimeMillis(), null, + 0, 0); + topology.addOrUpdateLink(lt, info); + + // check invariants hold + assertNotNull(topology.switchLinks.get(lt.getSrc().getSw())); + assertTrue(topology.switchLinks.get(lt.getSrc().getSw()).contains(lt)); + assertNotNull(topology.portLinks.get(lt.getSrc())); + assertTrue(topology.portLinks.get(lt.getSrc()).contains(lt)); + assertNotNull(topology.portLinks.get(lt.getDst())); + assertTrue(topology.portLinks.get(lt.getDst()).contains(lt)); + assertTrue(topology.links.containsKey(lt)); + + // check that it clears from memory + getMockFloodlightProvider().dispatchRoleChanged(null, Role.SLAVE); + assertTrue(topology.switchLinks.isEmpty()); + getMockFloodlightProvider().dispatchRoleChanged(Role.SLAVE, Role.MASTER); + // check that lldps were sent + assertTrue(ldm.isSendLLDPsCalled); + ldm.reset(); + } } diff --git a/src/test/java/net/floodlightcontroller/staticflowentry/StaticFlowTests.java b/src/test/java/net/floodlightcontroller/staticflowentry/StaticFlowTests.java index 9227eff8f581f2e2910836ce97ee9f40a2fd677b..186fd69699f1a4490fedd4f4a73569cd46b93fed 100644 --- a/src/test/java/net/floodlightcontroller/staticflowentry/StaticFlowTests.java +++ b/src/test/java/net/floodlightcontroller/staticflowentry/StaticFlowTests.java @@ -22,6 +22,7 @@ import org.openflow.util.HexString; import net.floodlightcontroller.core.FloodlightContext; +import net.floodlightcontroller.core.IFloodlightProviderService.Role; import net.floodlightcontroller.core.IOFSwitch; import net.floodlightcontroller.core.module.FloodlightModuleContext; import net.floodlightcontroller.core.module.FloodlightModuleException; @@ -293,4 +294,41 @@ public class StaticFlowTests extends FloodlightTestCase { return storage; } + + @Test + public void testHARoleChanged() throws IOException { + StaticFlowEntryPusher staticFlowEntryPusher = new StaticFlowEntryPusher(); + IStorageSourceService storage = createStorageWithFlowEntries(); + MockFloodlightProvider mfp = getMockFloodlightProvider(); + staticFlowEntryPusher.setFloodlightProvider(mfp); + staticFlowEntryPusher.setStorageSource(storage); + RestApiServer restApi = new RestApiServer(); + try { + FloodlightModuleContext fmc = new FloodlightModuleContext(); + restApi.init(fmc); + } catch (FloodlightModuleException e) { + e.printStackTrace(); + } + staticFlowEntryPusher.restApi = restApi; + staticFlowEntryPusher.startUp(null); // again, to hack unittest + + assert(staticFlowEntryPusher.entry2dpid.containsValue(TestSwitch1DPID)); + assert(staticFlowEntryPusher.entriesFromStorage.containsValue(FlowMod1)); + assert(staticFlowEntryPusher.entriesFromStorage.containsValue(FlowMod2)); + assert(staticFlowEntryPusher.entriesFromStorage.containsValue(FlowMod3)); + + // Send a notification that we've changed to slave + mfp.dispatchRoleChanged(null, Role.SLAVE); + // Make sure we've removed all our entries + assert(staticFlowEntryPusher.entry2dpid.isEmpty()); + assert(staticFlowEntryPusher.entriesFromStorage.isEmpty()); + + // Send a notification that we've changed to master + mfp.dispatchRoleChanged(Role.SLAVE, Role.MASTER); + // Make sure we've learned the entries + assert(staticFlowEntryPusher.entry2dpid.containsValue(TestSwitch1DPID)); + assert(staticFlowEntryPusher.entriesFromStorage.containsValue(FlowMod1)); + assert(staticFlowEntryPusher.entriesFromStorage.containsValue(FlowMod2)); + assert(staticFlowEntryPusher.entriesFromStorage.containsValue(FlowMod3)); + } } diff --git a/src/test/java/net/floodlightcontroller/test/FloodlightTestCase.java b/src/test/java/net/floodlightcontroller/test/FloodlightTestCase.java index ac711dbd3837d2109c45905761798df975cef2e1..7b81d42178845c944bc9932779b344c18330b9a4 100644 --- a/src/test/java/net/floodlightcontroller/test/FloodlightTestCase.java +++ b/src/test/java/net/floodlightcontroller/test/FloodlightTestCase.java @@ -23,6 +23,7 @@ import net.floodlightcontroller.core.IFloodlightProviderService; import net.floodlightcontroller.core.test.MockFloodlightProvider; import net.floodlightcontroller.packet.Ethernet; +import org.junit.Test; import org.openflow.protocol.OFMessage; import org.openflow.protocol.OFPacketIn; import org.openflow.protocol.OFType; @@ -65,11 +66,8 @@ public class FloodlightTestCase extends TestCase { public void setUp() throws Exception { mockFloodlightProvider = new MockFloodlightProvider(); } - - /** - * @return the applicationContext - */ + @Test public void testSanity() { assertTrue(true); } diff --git a/src/test/java/net/floodlightcontroller/topology/TopologyManagerTest.java b/src/test/java/net/floodlightcontroller/topology/TopologyManagerTest.java index 816eadea8ec7ba08b85c0f2ce5c401d7385cf04e..bcc886a42f1670371032d6e9e79e521ea838c4ec 100644 --- a/src/test/java/net/floodlightcontroller/topology/TopologyManagerTest.java +++ b/src/test/java/net/floodlightcontroller/topology/TopologyManagerTest.java @@ -1,11 +1,11 @@ package net.floodlightcontroller.topology; -import static org.junit.Assert.*; import net.floodlightcontroller.core.IFloodlightProviderService; +import net.floodlightcontroller.core.IFloodlightProviderService.Role; import net.floodlightcontroller.core.module.FloodlightModuleContext; -import net.floodlightcontroller.core.test.MockFloodlightProvider; import net.floodlightcontroller.core.test.MockThreadPoolService; import net.floodlightcontroller.linkdiscovery.ILinkDiscovery; +import net.floodlightcontroller.test.FloodlightTestCase; import net.floodlightcontroller.threadpool.IThreadPoolService; import net.floodlightcontroller.topology.TopologyManager; @@ -14,32 +14,26 @@ import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class TopologyManagerTest { +public class TopologyManagerTest extends FloodlightTestCase { protected static Logger log = LoggerFactory.getLogger(TopologyManagerTest.class); - TopologyManager topologyManager; + TopologyManager tm; FloodlightModuleContext fmc; - protected MockFloodlightProvider mockFloodlightProvider; - @Before - public void SetUp() throws Exception { - mockFloodlightProvider = new MockFloodlightProvider(); + @Before + public void setUp() throws Exception { + super.setUp(); fmc = new FloodlightModuleContext(); - fmc.addService(IFloodlightProviderService.class, mockFloodlightProvider); + fmc.addService(IFloodlightProviderService.class, getMockFloodlightProvider()); MockThreadPoolService tp = new MockThreadPoolService(); fmc.addService(IThreadPoolService.class, tp); - topologyManager = new TopologyManager(); + tm = new TopologyManager(); tp.init(fmc); - topologyManager.init(fmc); + tm.init(fmc); tp.startUp(fmc); } - public TopologyManager getTopologyManager() { - return topologyManager; - } - @Test - public void basicTest1() { - TopologyManager tm = getTopologyManager(); + public void testBasic1() throws Exception { tm.addOrUpdateLink((long)1, (short)1, (long)2, (short)1, ILinkDiscovery.LinkType.DIRECT_LINK); assertTrue(tm.getSwitchPorts().size() == 2); // for two nodes. assertTrue(tm.getSwitchPorts().get((long)1).size()==1); @@ -89,8 +83,7 @@ public class TopologyManagerTest { } @Test - public void basicTest2() { - TopologyManager tm = getTopologyManager(); + public void testBasic2() throws Exception { tm.addOrUpdateLink((long)1, (short)1, (long)2, (short)1, ILinkDiscovery.LinkType.DIRECT_LINK); tm.addOrUpdateLink((long)2, (short)2, (long)3, (short)1, ILinkDiscovery.LinkType.MULTIHOP_LINK); tm.addOrUpdateLink((long)3, (short)2, (long)1, (short)2, ILinkDiscovery.LinkType.TUNNEL); @@ -136,4 +129,14 @@ public class TopologyManagerTest { assertTrue(tm.getPortBroadcastDomainLinks().size()==0); assertTrue(tm.getTunnelLinks().size()==0); } + + @Test + public void testHARoleChange() throws Exception { + testBasic2(); + getMockFloodlightProvider().dispatchRoleChanged(null, Role.SLAVE); + assert(tm.switchPorts.isEmpty()); + assert(tm.switchPortLinks.isEmpty()); + assert(tm.portBroadcastDomainLinks.isEmpty()); + assert(tm.tunnelLinks.isEmpty()); + } }