diff --git a/src/main/java/net/floodlightcontroller/core/internal/Controller.java b/src/main/java/net/floodlightcontroller/core/internal/Controller.java index e8656c42b76853a220694358c67a3329b954a3f4..e6ecec4c529b8553fc117a33b19af4760cd98cfb 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/Controller.java +++ b/src/main/java/net/floodlightcontroller/core/internal/Controller.java @@ -166,6 +166,8 @@ public class Controller implements IFloodlightProviderService, // Configuration options protected int openFlowPort = 6633; protected int workerThreads = 0; + // The id for this controller node. Should be unique for each controller + // node in a controller cluster. protected String controllerId = "localhost"; // The current role of the controller. @@ -418,15 +420,42 @@ public class Controller implements IFloodlightProviderService, * @throws IOException */ protected void sendRoleRequest(IOFSwitch sw, Role role) throws IOException { + // There are three cases to consider: + // + // 1) If the controller role at the point the switch connected was + // null/disabled, then we never sent the role request probe to the + // switch and therefore never set the SWITCH_SUPPORTS_NX_ROLE + // attribute for the switch, so supportsNxRole is null. In that + // case since we're now enabling role support for the controller + // we should send out the role request probe/update to the switch. + // + // 2) If supportsNxRole == Boolean.TRUE then that means we've already + // sent the role request probe to the switch and it replied with + // a role reply message, so we know it supports role request + // messages. Now we're changing the role and we want to send + // it another role request message to inform it of the new role + // for the controller. + // + // 3) If supportsNxRole == Boolean.FALSE, then that means we sent the + // role request probe to the switch but it responded with an error + // indicating that it didn't understand the role request message. + // In that case we don't want to send it another role request that + // it (still) doesn't understand. But if the new role of the + // controller is SLAVE, then we don't want the switch to remain + // connected to this controller. It might support the older serial + // failover model for HA support, so we want to terminate the + // connection and get it to initiate a connection with another + // controller in its list of controllers. Eventually (hopefully, if + // things are configured correctly) it will walk down its list of + // controllers and connect to the current master controller. Boolean supportsNxRole = (Boolean) sw.getAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE); - if ((supportsNxRole != null) && supportsNxRole) { + if ((supportsNxRole == null) || supportsNxRole) { + // Handle cases #1 and #2 sendNxRoleRequest(sw, role); - } else if (supportsNxRole != null && !supportsNxRole) { - // We know switch does not support role-request (and so sw.role is null) - // but we may have just switched roles from MASTER to SLAVE in which - // case we should disconnect switch - if (getRole() == Role.SLAVE && sw.getRole() == null) { + } else { + // Handle case #3 + if (getRole() == Role.SLAVE) { log.error("Disconnecting switch {} that doesn't support " + "role request messages from a controller that went to SLAVE mode"); // Closing the channel should result in a call to @@ -1704,7 +1733,11 @@ public class Controller implements IFloodlightProviderService, roleString = properties.getProperty("floodlight.role"); } catch (IOException exc) { - log.error("Error reading current role value from file: {}", rolePath); + // Don't treat it as an error if the file specified by the + // rolepath property doesn't exist. This lets us enable the + // HA mechanism by just creating/setting the floodlight.role + // property in that file without having to modify the + // floodlight properties. } } } diff --git a/src/main/java/net/floodlightcontroller/core/web/RoleResource.java b/src/main/java/net/floodlightcontroller/core/web/ControllerRoleResource.java similarity index 72% rename from src/main/java/net/floodlightcontroller/core/web/RoleResource.java rename to src/main/java/net/floodlightcontroller/core/web/ControllerRoleResource.java index d0bf2f837e62be1d4b915a1f23b60eb36e66d92d..2a1520577c0ebbb581f57b8989e71539acccbaa0 100644 --- a/src/main/java/net/floodlightcontroller/core/web/RoleResource.java +++ b/src/main/java/net/floodlightcontroller/core/web/ControllerRoleResource.java @@ -11,33 +11,10 @@ import org.restlet.resource.Post; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class RoleResource extends ServerResource { +public class ControllerRoleResource extends ServerResource { - protected static Logger log = LoggerFactory.getLogger(RoleResource.class); + protected static Logger log = LoggerFactory.getLogger(ControllerRoleResource.class); - public static class RoleInfo { - protected String role; - - public RoleInfo() { - } - - public RoleInfo(String role) { - setRole(role); - } - - public RoleInfo(Role role) { - this.role = (role != null) ? role.name() : "DISABLED"; - } - - public String getRole() { - return role; - } - - public void setRole(String role) { - this.role = role; - } - } - @Get("json") public RoleInfo getRole() { IFloodlightProviderService floodlightProvider = diff --git a/src/main/java/net/floodlightcontroller/core/web/CoreWebRoutable.java b/src/main/java/net/floodlightcontroller/core/web/CoreWebRoutable.java index 66c462618ab10097892e680a8838d61a287bcde5..009681fb4693fcb83a3292d1babe319058cb3778 100644 --- a/src/main/java/net/floodlightcontroller/core/web/CoreWebRoutable.java +++ b/src/main/java/net/floodlightcontroller/core/web/CoreWebRoutable.java @@ -39,6 +39,7 @@ public class CoreWebRoutable implements RestletRoutable { Router router = new Router(context); router.attach("/module/all/json", ModuleLoaderResource.class); router.attach("/module/loaded/json", LoadedModuleLoaderResource.class); + router.attach("/switch/{switchId}/role/json", SwitchRoleResource.class); router.attach("/switch/all/{statType}/json", AllSwitchStatisticsResource.class); router.attach("/switch/{switchId}/{statType}/json", SwitchStatisticsResource.class); router.attach("/controller/switches/json", ControllerSwitchesResource.class); @@ -56,7 +57,7 @@ public class CoreWebRoutable implements RestletRoutable { EventHistoryTopologyClusterResource.class); router.attach("/storage/tables/json", StorageSourceTablesResource.class); router.attach("/controller/summary/json", ControllerSummaryResource.class); - router.attach("/role/json", RoleResource.class); + router.attach("/role/json", ControllerRoleResource.class); router.attach("/health/json", HealthCheckResource.class); return router; } diff --git a/src/main/java/net/floodlightcontroller/core/web/RoleInfo.java b/src/main/java/net/floodlightcontroller/core/web/RoleInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..e600ea0eb02ada3d8003fb4c4e75b3e8b57f7b16 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/web/RoleInfo.java @@ -0,0 +1,26 @@ +package net.floodlightcontroller.core.web; + +import net.floodlightcontroller.core.IFloodlightProviderService.Role; + +public class RoleInfo { + protected String role; + + public RoleInfo() { + } + + public RoleInfo(String role) { + setRole(role); + } + + public RoleInfo(Role role) { + this.role = (role != null) ? role.name() : "DISABLED"; + } + + public String getRole() { + return role; + } + + public void setRole(String role) { + this.role = role; + } +} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/core/web/SwitchRoleResource.java b/src/main/java/net/floodlightcontroller/core/web/SwitchRoleResource.java new file mode 100644 index 0000000000000000000000000000000000000000..0d73f93f12130c1df48fc995b1ccd50ad53ef82e --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/web/SwitchRoleResource.java @@ -0,0 +1,46 @@ +package net.floodlightcontroller.core.web; + +import java.util.HashMap; + +import org.openflow.util.HexString; +import org.restlet.resource.ServerResource; + +import net.floodlightcontroller.core.IFloodlightProviderService; +import net.floodlightcontroller.core.IOFSwitch; + +import org.restlet.resource.Get; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SwitchRoleResource extends ServerResource { + + protected static Logger log = LoggerFactory.getLogger(SwitchRoleResource.class); + + @Get("json") + public Object getRole() { + IFloodlightProviderService floodlightProvider = + (IFloodlightProviderService)getContext().getAttributes(). + get(IFloodlightProviderService.class.getCanonicalName()); + + String switchId = (String) getRequestAttributes().get("switchId"); + + RoleInfo roleInfo; + + if (switchId.equalsIgnoreCase("all")) { + HashMap<String,RoleInfo> model = new HashMap<String,RoleInfo>(); + for (IOFSwitch sw: floodlightProvider.getSwitches().values()) { + switchId = sw.getStringId(); + roleInfo = new RoleInfo(sw.getRole()); + model.put(switchId, roleInfo); + } + return model; + } + + Long dpid = HexString.toLong(switchId); + IOFSwitch sw = floodlightProvider.getSwitches().get(dpid); + if (sw == null) + return null; + roleInfo = new RoleInfo(sw.getRole()); + return roleInfo; + } +} diff --git a/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java b/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java index 3eeb0681f57a9c3f444703b9b99c6f1c1b3b73af..9fe7bfdfd17ea5d583df23701eeb5bdab4e37046 100644 --- a/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java +++ b/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java @@ -694,7 +694,6 @@ public class ControllerTest extends FloodlightTestCase { // newsw.role is null because the switch does not support // role request messages - expect(newsw.getRole()).andReturn(null); expect(newsw.getAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE)) .andReturn(false); // switch is connected