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 {
public static final String CONTEXT_PI_PAYLOAD =
"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
* packet-in payload
......@@ -67,11 +72,22 @@ public interface IFloodlightProviderService extends IFloodlightService {
public void removeOFMessageListener(OFType type, IOFMessageListener listener);
/**
* Returns a list of all actively connected OpenFlow switches
* @return the set of connected switches
* Returns a list of all actively connected OpenFlow switches. This doesn't
* 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();
/**
* 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
* @param listener
......@@ -122,7 +138,7 @@ public interface IFloodlightProviderService extends IFloodlightService {
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 m the message
* @param bc any accompanying context object
......
......@@ -22,6 +22,7 @@ import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;
import net.floodlightcontroller.core.IFloodlightProviderService.Role;
import net.floodlightcontroller.core.types.MacVlanPair;
import net.floodlightcontroller.util.TimedCache;
......@@ -42,6 +43,7 @@ public interface IOFSwitch {
// Attribute keys
public static final String SWITCH_DESCRIPTION_FUTURE = "DescriptionFuture";
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 PROP_FASTWILDCARDS = "FastWildcards";
public static final String PROP_REQUIRES_L3_MATCH = "requiresL3Match";
......@@ -241,6 +243,25 @@ public interface IOFSwitch {
*/
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
* @param reply the reply to deliver
......
......@@ -52,4 +52,12 @@ class OFChannelState {
protected volatile HandshakeState hsState = HandshakeState.START;
protected boolean hasGetConfigReply = 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;
import net.floodlightcontroller.core.FloodlightContext;
import net.floodlightcontroller.core.IFloodlightProviderService;
import net.floodlightcontroller.core.IOFMessageListener;
import net.floodlightcontroller.core.IFloodlightProviderService.Role;
import net.floodlightcontroller.core.IOFSwitch;
import net.floodlightcontroller.core.types.MacVlanPair;
import net.floodlightcontroller.util.TimedCache;
......@@ -76,8 +77,9 @@ public class OFSwitchImpl implements IOFSwitch {
protected Map<Integer,OFStatisticsFuture> statsFutureMap;
protected Map<Integer, IOFMessageListener> iofMsgListenersMap;
protected boolean connected;
protected Role role;
protected TimedCache<Long> timedCache;
protected ReentrantReadWriteLock lock;
protected ReentrantReadWriteLock listenerLock;
public static IOFSwitchFeatures switchFeatures;
protected static final ThreadLocal<Map<OFSwitchImpl,List<OFMessage>>> local_msg_buffer =
......@@ -100,9 +102,9 @@ public class OFSwitchImpl implements IOFSwitch {
this.connected = true;
this.statsFutureMap = new ConcurrentHashMap<Integer,OFStatisticsFuture>();
this.iofMsgListenersMap = new ConcurrentHashMap<Integer,IOFMessageListener>();
this.role = null;
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
this.setAttribute(PROP_FASTWILDCARDS, (Integer) OFMatch.OFPFW_ALL);
this.setAttribute(PROP_SUPPORTS_OFPP_FLOOD, new Boolean(true));
......@@ -161,6 +163,19 @@ public class OFSwitchImpl implements IOFSwitch {
public void write(List<OFMessage> msglist, FloodlightContext bc) throws IOException {
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.write(msglist);
......@@ -390,6 +405,21 @@ public class OFSwitchImpl implements IOFSwitch {
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
public void setSwitchProperties(OFDescriptionStatistics description) {
if (switchFeatures != null) {
......@@ -444,21 +474,23 @@ public class OFSwitchImpl implements IOFSwitch {
}
/**
* Return a lock that need to be held while processing a message. Multiple threads
* can hold this lock.
* Return a read lock that must be held while calling the listeners for
* messages from the switch. Holding the read lock prevents the active
* switch list from being modified out from under the listeners.
* @return
*/
public Lock processMessageLock() {
return lock.readLock();
public Lock getListenerReadLock() {
return listenerLock.readLock();
}
/**
* Return a lock that needs to be held while the switch is removed asynchronously, i.e.,
* the removing is not triggered by events on this switch's channel.
* Mutex with processMessageLock
* Return a write lock that must be held when the controllers modifies the
* list of active switches. This is to ensure that the active switch list
* doesn't change out from under the listeners as they are handling a
* message from the switch.
* @return
*/
public Lock asyncRemoveSwitchLock() {
return lock.writeLock();
public Lock getListenerWriteLock() {
return listenerLock.writeLock();
}
}
......@@ -60,6 +60,8 @@ 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("/health/json", HealthCheckResource.class);
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
}
restlets.add(routable);
}
@Override
public void run(int restPort) {
RestApplication restApp = new RestApplication();
......
......@@ -42,7 +42,7 @@ public class OFError extends OFMessage implements OFMessageFactoryAware {
// generic support for extensible vendor-defined error messages.
// It uses the random value 0xb0c2 to avoid conflicts with other possible new
// 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);
protected short value;
......@@ -164,6 +164,22 @@ public class OFError extends OFMessage implements OFMessageFactoryAware {
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 {
// should only have one message embedded; if more than one, just
// grab first
......
......@@ -28,6 +28,7 @@ import org.openflow.protocol.vendor.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
*/
......
......@@ -46,7 +46,7 @@ public class OFRoleReplyVendorData extends OFRoleVendorData {
/**
* 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.
......
......@@ -46,7 +46,7 @@ public class OFRoleRequestVendorData extends OFRoleVendorData {
/**
* 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.
......
......@@ -420,7 +420,7 @@ public class ControllerTest extends FloodlightTestCase {
@Test
public void testAddSwitch() throws Exception {
controller.switches = new ConcurrentHashMap<Long, IOFSwitch>();
controller.activeSwitches = new ConcurrentHashMap<Long, IOFSwitch>();
//OFSwitchImpl oldsw = createMock(OFSwitchImpl.class);
OFSwitchImpl oldsw = new OFSwitchImpl();
......@@ -449,7 +449,7 @@ public class ControllerTest extends FloodlightTestCase {
expect(newsw.getFeaturesReply()).andReturn(new OFFeaturesReply()).anyTimes();
expect(newsw.getPorts()).andReturn(new HashMap<Short,OFPhysicalPort>());
controller.switches.put(0L, oldsw);
controller.activeSwitches.put(0L, oldsw);
replay(newsw, channel, channel2);
controller.addSwitch(newsw);
......
......@@ -232,6 +232,15 @@ public class MockFloodlightProvider implements IFloodlightModule, IFloodlightPro
return null;
}
@Override
public Role getRole() {
return null;
}
@Override
public void setRole(Role role) {
}
@Override
public
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