Skip to content
Snippets Groups Projects
Commit 2e9427e2 authored by Rob Vaterlaus's avatar Rob Vaterlaus
Browse files

Initial support for OVS-style role request messages and maintaining...

Initial support for OVS-style role request messages and maintaining connections with switches in a backup role [#23366119].
Added REST API to get/set the current role of the controller [#2336329].
Added simple health check REST API [#23364299].
Fixed off-by-one error in the message code values for the role request/reply messages.
parent 4d43e2e8
No related branches found
No related tags found
No related merge requests found
Showing
with 815 additions and 148 deletions
...@@ -44,7 +44,12 @@ public interface IFloodlightProviderService extends IFloodlightService { ...@@ -44,7 +44,12 @@ public interface IFloodlightProviderService extends IFloodlightService {
public static final String CONTEXT_PI_PAYLOAD = public static final String CONTEXT_PI_PAYLOAD =
"net.floodlightcontroller.core.IFloodlightProvider.piPayload"; "net.floodlightcontroller.core.IFloodlightProvider.piPayload";
/**
* The role of the controller as used by the OF 1.2 and OVS failover and
* load-balancing mechanism.
*/
public static enum Role { EQUAL, MASTER, SLAVE };
/** /**
* A FloodlightContextStore object that can be used to retrieve the * A FloodlightContextStore object that can be used to retrieve the
* packet-in payload * packet-in payload
...@@ -67,11 +72,22 @@ public interface IFloodlightProviderService extends IFloodlightService { ...@@ -67,11 +72,22 @@ public interface IFloodlightProviderService extends IFloodlightService {
public void removeOFMessageListener(OFType type, IOFMessageListener listener); public void removeOFMessageListener(OFType type, IOFMessageListener listener);
/** /**
* Returns a list of all actively connected OpenFlow switches * Returns a list of all actively connected OpenFlow switches. This doesn't
* @return the set of connected switches * contain switches that are connected but the controller's in the slave role.
* @return the set of actively connected switches
*/ */
public Map<Long, IOFSwitch> getSwitches(); public Map<Long, IOFSwitch> getSwitches();
/**
* Get the current role of the controller
*/
public Role getRole();
/**
* Set the role of the controller
*/
public void setRole(Role role);
/** /**
* Add a switch listener * Add a switch listener
* @param listener * @param listener
...@@ -122,7 +138,7 @@ public interface IFloodlightProviderService extends IFloodlightService { ...@@ -122,7 +138,7 @@ public interface IFloodlightProviderService extends IFloodlightService {
FloodlightContext bContext); FloodlightContext bContext);
/** /**
* Process written messages through the message listeners for the contoller * Process written messages through the message listeners for the controller
* @param sw The switch being written to * @param sw The switch being written to
* @param m the message * @param m the message
* @param bc any accompanying context object * @param bc any accompanying context object
......
...@@ -22,6 +22,7 @@ import java.util.Date; ...@@ -22,6 +22,7 @@ import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import net.floodlightcontroller.core.IFloodlightProviderService.Role;
import net.floodlightcontroller.core.types.MacVlanPair; import net.floodlightcontroller.core.types.MacVlanPair;
import net.floodlightcontroller.util.TimedCache; import net.floodlightcontroller.util.TimedCache;
...@@ -42,6 +43,7 @@ public interface IOFSwitch { ...@@ -42,6 +43,7 @@ public interface IOFSwitch {
// Attribute keys // Attribute keys
public static final String SWITCH_DESCRIPTION_FUTURE = "DescriptionFuture"; public static final String SWITCH_DESCRIPTION_FUTURE = "DescriptionFuture";
public static final String SWITCH_DESCRIPTION_DATA = "DescriptionData"; public static final String SWITCH_DESCRIPTION_DATA = "DescriptionData";
public static final String SWITCH_SUPPORTS_NX_ROLE = "supportsNxRole";
public static final String SWITCH_IS_CORE_SWITCH = "isCoreSwitch"; public static final String SWITCH_IS_CORE_SWITCH = "isCoreSwitch";
public static final String PROP_FASTWILDCARDS = "FastWildcards"; public static final String PROP_FASTWILDCARDS = "FastWildcards";
public static final String PROP_REQUIRES_L3_MATCH = "requiresL3Match"; public static final String PROP_REQUIRES_L3_MATCH = "requiresL3Match";
...@@ -241,6 +243,25 @@ public interface IOFSwitch { ...@@ -241,6 +243,25 @@ public interface IOFSwitch {
*/ */
public void setConnected(boolean connected); public void setConnected(boolean connected);
/**
* Get the current role of the controller for the switch
* @return the role of the controller
*/
public Role getRole();
/**
* Set the role of the controller for the switch
* @param role controller role
*/
public void setRole(Role role);
/**
* Check if the controller is an active controller for the switch.
* The controller is active if its role is MASTER or EQUAL.
* @return whether the controller is active
*/
public boolean isActive();
/** /**
* Deliver the statistics future reply * Deliver the statistics future reply
* @param reply the reply to deliver * @param reply the reply to deliver
......
...@@ -52,4 +52,12 @@ class OFChannelState { ...@@ -52,4 +52,12 @@ class OFChannelState {
protected volatile HandshakeState hsState = HandshakeState.START; protected volatile HandshakeState hsState = HandshakeState.START;
protected boolean hasGetConfigReply = false; protected boolean hasGetConfigReply = false;
protected boolean hasDescription = false; protected boolean hasDescription = false;
// The hasNxRoleReply flag doesn't mean that the switch supports the NX
// role messages, just that we've received an answer back from the
// switch (possibly a bad vendor error) in response to our initial
// role request. It's used as a flag to indicate that we've met one
// of the conditions necessary to advance the handshake state to READY.
protected boolean hasNxRoleReply = false;
protected int nxRoleRequestXid;
} }
\ No newline at end of file
...@@ -33,6 +33,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; ...@@ -33,6 +33,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
import net.floodlightcontroller.core.FloodlightContext; import net.floodlightcontroller.core.FloodlightContext;
import net.floodlightcontroller.core.IFloodlightProviderService; import net.floodlightcontroller.core.IFloodlightProviderService;
import net.floodlightcontroller.core.IOFMessageListener; import net.floodlightcontroller.core.IOFMessageListener;
import net.floodlightcontroller.core.IFloodlightProviderService.Role;
import net.floodlightcontroller.core.IOFSwitch; import net.floodlightcontroller.core.IOFSwitch;
import net.floodlightcontroller.core.types.MacVlanPair; import net.floodlightcontroller.core.types.MacVlanPair;
import net.floodlightcontroller.util.TimedCache; import net.floodlightcontroller.util.TimedCache;
...@@ -76,8 +77,9 @@ public class OFSwitchImpl implements IOFSwitch { ...@@ -76,8 +77,9 @@ public class OFSwitchImpl implements IOFSwitch {
protected Map<Integer,OFStatisticsFuture> statsFutureMap; protected Map<Integer,OFStatisticsFuture> statsFutureMap;
protected Map<Integer, IOFMessageListener> iofMsgListenersMap; protected Map<Integer, IOFMessageListener> iofMsgListenersMap;
protected boolean connected; protected boolean connected;
protected Role role;
protected TimedCache<Long> timedCache; protected TimedCache<Long> timedCache;
protected ReentrantReadWriteLock lock; protected ReentrantReadWriteLock listenerLock;
public static IOFSwitchFeatures switchFeatures; public static IOFSwitchFeatures switchFeatures;
protected static final ThreadLocal<Map<OFSwitchImpl,List<OFMessage>>> local_msg_buffer = protected static final ThreadLocal<Map<OFSwitchImpl,List<OFMessage>>> local_msg_buffer =
...@@ -100,9 +102,9 @@ public class OFSwitchImpl implements IOFSwitch { ...@@ -100,9 +102,9 @@ public class OFSwitchImpl implements IOFSwitch {
this.connected = true; this.connected = true;
this.statsFutureMap = new ConcurrentHashMap<Integer,OFStatisticsFuture>(); this.statsFutureMap = new ConcurrentHashMap<Integer,OFStatisticsFuture>();
this.iofMsgListenersMap = new ConcurrentHashMap<Integer,IOFMessageListener>(); this.iofMsgListenersMap = new ConcurrentHashMap<Integer,IOFMessageListener>();
this.role = null;
this.timedCache = new TimedCache<Long>(100, 5*1000 ); // 5 seconds interval this.timedCache = new TimedCache<Long>(100, 5*1000 ); // 5 seconds interval
this.lock = new ReentrantReadWriteLock(); this.listenerLock = new ReentrantReadWriteLock();
// Defaults properties for an ideal switch // Defaults properties for an ideal switch
this.setAttribute(PROP_FASTWILDCARDS, (Integer) OFMatch.OFPFW_ALL); this.setAttribute(PROP_FASTWILDCARDS, (Integer) OFMatch.OFPFW_ALL);
this.setAttribute(PROP_SUPPORTS_OFPP_FLOOD, new Boolean(true)); this.setAttribute(PROP_SUPPORTS_OFPP_FLOOD, new Boolean(true));
...@@ -161,6 +163,19 @@ public class OFSwitchImpl implements IOFSwitch { ...@@ -161,6 +163,19 @@ public class OFSwitchImpl implements IOFSwitch {
public void write(List<OFMessage> msglist, FloodlightContext bc) throws IOException { public void write(List<OFMessage> msglist, FloodlightContext bc) throws IOException {
for (OFMessage m : msglist) { for (OFMessage m : msglist) {
if (role == Role.SLAVE) {
switch (m.getType()) {
case PACKET_OUT:
case FLOW_MOD:
case PORT_MOD:
log.warn("Sending OF message that modifies switch state while in the slave role: {}", m.getType().name());
break;
default:
break;
}
}
// FIXME: Debugging code should be disabled!!!
// log.debug("Sending message type {} with xid {}", new Object[] {m.getType(), m.getXid()});
this.floodlightProvider.handleOutgoingMessage(this, m, bc); this.floodlightProvider.handleOutgoingMessage(this, m, bc);
} }
this.write(msglist); this.write(msglist);
...@@ -390,6 +405,21 @@ public class OFSwitchImpl implements IOFSwitch { ...@@ -390,6 +405,21 @@ public class OFSwitchImpl implements IOFSwitch {
this.connected = connected; this.connected = connected;
} }
@Override
public synchronized Role getRole() {
return role;
}
@Override
public synchronized void setRole(Role role) {
this.role = role;
}
@Override
public synchronized boolean isActive() {
return (role != Role.SLAVE);
}
@Override @Override
public void setSwitchProperties(OFDescriptionStatistics description) { public void setSwitchProperties(OFDescriptionStatistics description) {
if (switchFeatures != null) { if (switchFeatures != null) {
...@@ -444,21 +474,23 @@ public class OFSwitchImpl implements IOFSwitch { ...@@ -444,21 +474,23 @@ public class OFSwitchImpl implements IOFSwitch {
} }
/** /**
* Return a lock that need to be held while processing a message. Multiple threads * Return a read lock that must be held while calling the listeners for
* can hold this lock. * messages from the switch. Holding the read lock prevents the active
* switch list from being modified out from under the listeners.
* @return * @return
*/ */
public Lock processMessageLock() { public Lock getListenerReadLock() {
return lock.readLock(); return listenerLock.readLock();
} }
/** /**
* Return a lock that needs to be held while the switch is removed asynchronously, i.e., * Return a write lock that must be held when the controllers modifies the
* the removing is not triggered by events on this switch's channel. * list of active switches. This is to ensure that the active switch list
* Mutex with processMessageLock * doesn't change out from under the listeners as they are handling a
* message from the switch.
* @return * @return
*/ */
public Lock asyncRemoveSwitchLock() { public Lock getListenerWriteLock() {
return lock.writeLock(); return listenerLock.writeLock();
} }
} }
...@@ -60,6 +60,8 @@ public class CoreWebRoutable implements RestletRoutable { ...@@ -60,6 +60,8 @@ public class CoreWebRoutable implements RestletRoutable {
EventHistoryTopologyClusterResource.class); EventHistoryTopologyClusterResource.class);
router.attach("/storage/tables/json", StorageSourceTablesResource.class); router.attach("/storage/tables/json", StorageSourceTablesResource.class);
router.attach("/controller/summary/json", ControllerSummaryResource.class); router.attach("/controller/summary/json", ControllerSummaryResource.class);
router.attach("/role/json", RoleResource.class);
router.attach("/health/json", HealthCheckResource.class);
return router; return router;
} }
} }
package net.floodlightcontroller.core.web;
import org.restlet.resource.Get;
import org.restlet.resource.ServerResource;
public class HealthCheckResource extends ServerResource {
public static class HealthCheckInfo {
protected boolean healthy;
public HealthCheckInfo() {
this.healthy = true;
}
public boolean isHealthy() {
return healthy;
}
public void setHealthy(boolean healthy) {
this.healthy = healthy;
}
}
@Get("json")
public HealthCheckInfo healthCheck() {
// Currently this is the simplest possible health check -- basically
// just that the controller is still running and able to respond to
// REST calls.
// Eventually this should be more sophisticated and do things
// like monitoring internal data structures of the controller
// (e.g. async storage queue length).
return new HealthCheckInfo();
}
}
package net.floodlightcontroller.core.web;
import org.restlet.data.Status;
import org.restlet.resource.ServerResource;
import net.floodlightcontroller.core.IFloodlightProviderService;
import net.floodlightcontroller.core.IFloodlightProviderService.Role;
import org.restlet.resource.Get;
import org.restlet.resource.Post;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RoleResource extends ServerResource {
protected static Logger log = LoggerFactory.getLogger(RoleResource.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 =
(IFloodlightProviderService)getContext().getAttributes().
get(IFloodlightProviderService.class.getCanonicalName());
return new RoleInfo(floodlightProvider.getRole());
}
@Post("json")
public void setRole(RoleInfo roleInfo) {
//Role role = Role.lookupRole(roleInfo.getRole());
Role role = null;
try {
role = Role.valueOf(roleInfo.getRole().toUpperCase());
}
catch (IllegalArgumentException e) {
// The role value in the REST call didn't match a valid
// role name, so just leave the role as null and handle
// the error below.
}
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");
}
IFloodlightProviderService floodlightProvider =
(IFloodlightProviderService)getContext().getAttributes().
get(IFloodlightProviderService.class.getCanonicalName());
floodlightProvider.setRole(role);
}
}
...@@ -105,7 +105,7 @@ public class RestApiServer ...@@ -105,7 +105,7 @@ public class RestApiServer
} }
restlets.add(routable); restlets.add(routable);
} }
@Override @Override
public void run(int restPort) { public void run(int restPort) {
RestApplication restApp = new RestApplication(); RestApplication restApp = new RestApplication();
......
...@@ -42,7 +42,7 @@ public class OFError extends OFMessage implements OFMessageFactoryAware { ...@@ -42,7 +42,7 @@ public class OFError extends OFMessage implements OFMessageFactoryAware {
// generic support for extensible vendor-defined error messages. // generic support for extensible vendor-defined error messages.
// It uses the random value 0xb0c2 to avoid conflicts with other possible new // It uses the random value 0xb0c2 to avoid conflicts with other possible new
// error types. Support for vendor-defined extended errors has been standardized // error types. Support for vendor-defined extended errors has been standardized
// in the OF 1.2 spec, so this workaround in only needed for 1.0. // in the OF 1.2 spec, so this workaround is only needed for 1.0.
OFPET_HELLO_FAILED, OFPET_BAD_REQUEST, OFPET_BAD_ACTION, OFPET_FLOW_MOD_FAILED, OFPET_PORT_MOD_FAILED, OFPET_QUEUE_OP_FAILED, OFPET_VENDOR_ERROR((short)0xb0c2); OFPET_HELLO_FAILED, OFPET_BAD_REQUEST, OFPET_BAD_ACTION, OFPET_FLOW_MOD_FAILED, OFPET_PORT_MOD_FAILED, OFPET_QUEUE_OP_FAILED, OFPET_VENDOR_ERROR((short)0xb0c2);
protected short value; protected short value;
...@@ -164,6 +164,22 @@ public class OFError extends OFMessage implements OFMessageFactoryAware { ...@@ -164,6 +164,22 @@ public class OFError extends OFMessage implements OFMessageFactoryAware {
this.errorCode = (short) code.ordinal(); this.errorCode = (short) code.ordinal();
} }
public int getVendorErrorType() {
return vendorErrorType;
}
public void setVendorErrorType(int vendorErrorType) {
this.vendorErrorType = vendorErrorType;
}
public short getVendorErrorCode() {
return vendorErrorCode;
}
public void setVendorErrorCode(short vendorErrorCode) {
this.vendorErrorCode = vendorErrorCode;
}
public OFMessage getOffendingMsg() throws MessageParseException { public OFMessage getOffendingMsg() throws MessageParseException {
// should only have one message embedded; if more than one, just // should only have one message embedded; if more than one, just
// grab first // grab first
......
...@@ -28,6 +28,7 @@ import org.openflow.protocol.vendor.OFVendorData; ...@@ -28,6 +28,7 @@ import org.openflow.protocol.vendor.OFVendorData;
*/ */
public class OFNiciraVendorData implements OFVendorData { public class OFNiciraVendorData implements OFVendorData {
public static final int NX_VENDOR_ID = 0x00002320;
/** /**
* The value of the integer data type at the beginning of the vendor data * The value of the integer data type at the beginning of the vendor data
*/ */
......
...@@ -46,7 +46,7 @@ public class OFRoleReplyVendorData extends OFRoleVendorData { ...@@ -46,7 +46,7 @@ public class OFRoleReplyVendorData extends OFRoleVendorData {
/** /**
* The data type value for a role reply * The data type value for a role reply
*/ */
public static final int NXT_ROLE_REPLY = 10; public static final int NXT_ROLE_REPLY = 11;
/** /**
* Construct a role reply vendor data with an unspecified role value. * Construct a role reply vendor data with an unspecified role value.
......
...@@ -46,7 +46,7 @@ public class OFRoleRequestVendorData extends OFRoleVendorData { ...@@ -46,7 +46,7 @@ public class OFRoleRequestVendorData extends OFRoleVendorData {
/** /**
* The data type value for a role request * The data type value for a role request
*/ */
public static final int NXT_ROLE_REQUEST = 9; public static final int NXT_ROLE_REQUEST = 10;
/** /**
* Construct a role request vendor data with an unspecified role value. * Construct a role request vendor data with an unspecified role value.
......
...@@ -420,7 +420,7 @@ public class ControllerTest extends FloodlightTestCase { ...@@ -420,7 +420,7 @@ public class ControllerTest extends FloodlightTestCase {
@Test @Test
public void testAddSwitch() throws Exception { public void testAddSwitch() throws Exception {
controller.switches = new ConcurrentHashMap<Long, IOFSwitch>(); controller.activeSwitches = new ConcurrentHashMap<Long, IOFSwitch>();
//OFSwitchImpl oldsw = createMock(OFSwitchImpl.class); //OFSwitchImpl oldsw = createMock(OFSwitchImpl.class);
OFSwitchImpl oldsw = new OFSwitchImpl(); OFSwitchImpl oldsw = new OFSwitchImpl();
...@@ -449,7 +449,7 @@ public class ControllerTest extends FloodlightTestCase { ...@@ -449,7 +449,7 @@ public class ControllerTest extends FloodlightTestCase {
expect(newsw.getFeaturesReply()).andReturn(new OFFeaturesReply()).anyTimes(); expect(newsw.getFeaturesReply()).andReturn(new OFFeaturesReply()).anyTimes();
expect(newsw.getPorts()).andReturn(new HashMap<Short,OFPhysicalPort>()); expect(newsw.getPorts()).andReturn(new HashMap<Short,OFPhysicalPort>());
controller.switches.put(0L, oldsw); controller.activeSwitches.put(0L, oldsw);
replay(newsw, channel, channel2); replay(newsw, channel, channel2);
controller.addSwitch(newsw); controller.addSwitch(newsw);
......
...@@ -232,6 +232,15 @@ public class MockFloodlightProvider implements IFloodlightModule, IFloodlightPro ...@@ -232,6 +232,15 @@ public class MockFloodlightProvider implements IFloodlightModule, IFloodlightPro
return null; return null;
} }
@Override
public Role getRole() {
return null;
}
@Override
public void setRole(Role role) {
}
@Override @Override
public public
void void
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment