diff --git a/src/main/java/net/floodlightcontroller/core/IFloodlightProviderService.java b/src/main/java/net/floodlightcontroller/core/IFloodlightProviderService.java index b9a628ba84d16c812d6b7309696eb05251f885f1..67b54290dc237baa634a822a77c264dc8bbdc0eb 100644 --- a/src/main/java/net/floodlightcontroller/core/IFloodlightProviderService.java +++ b/src/main/java/net/floodlightcontroller/core/IFloodlightProviderService.java @@ -17,6 +17,7 @@ package net.floodlightcontroller.core; +import java.io.IOException; import java.util.List; import java.util.Map; @@ -215,5 +216,17 @@ public interface IFloodlightProviderService extends */ public void addOFSwitchDriver(String desc, IOFSwitchDriver driver); + /** + * Send an HA role request to switch with the given paramaters in the + * form of Nicira Vendor extensions. + * + * @param sw Switch to send the request + * @param xid Transaction ID of the OF message + * @param role The role to set the switch to + * @param cookie Cookie from IOFSwitch.sendHARoleRequest + * @throws IOException + */ + public void sendNxRoleRequest(IOFSwitch sw, int xid, Role role, long cookie) + throws IOException; } diff --git a/src/main/java/net/floodlightcontroller/core/IOFSwitch.java b/src/main/java/net/floodlightcontroller/core/IOFSwitch.java index 04d46480f7580cdd4f98eee01c44c399498ab012..d57124578cf944cc8bdd936729e0d9ca29194ab0 100644 --- a/src/main/java/net/floodlightcontroller/core/IOFSwitch.java +++ b/src/main/java/net/floodlightcontroller/core/IOFSwitch.java @@ -391,14 +391,17 @@ public interface IOFSwitch { */ public int sendHARoleRequest(Role role, long cookie) throws IOException, HARoleUnsupportedException; - + /** - * Check HA role request cookie + * Set switch's HA role to role. The haRoleReplyReceived indicates + * if a reply was received from the switch (error replies excluded). * - * @param cookie - * @return + * If role is null, the switch should close the channel connection. + * + * @param role + * @param haRoleReplyReceived */ - public boolean checkFirstPendingRoleRequestCookie(long cookie); + public void setHARole(Role role, boolean haRoleReplyReceived); public void setChannel(Channel channel); @@ -406,13 +409,8 @@ public interface IOFSwitch { public void setThreadPoolService(IThreadPoolService threadPool); - public void deliverRoleReply(int xid, Role role); - - public void deliverRoleRequestNotSupported(int xid); - public Lock getListenerReadLock(); - public boolean checkFirstPendingRoleRequestXid(int xid); - public Lock getListenerWriteLock(); + } diff --git a/src/main/java/net/floodlightcontroller/core/internal/Controller.java b/src/main/java/net/floodlightcontroller/core/internal/Controller.java index cfe1d85bb60a96ef5e60ec732fd7b1953560c328..55eb924ddb6a596c6aa2a2845bce1faf46ba635f 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/Controller.java +++ b/src/main/java/net/floodlightcontroller/core/internal/Controller.java @@ -874,7 +874,7 @@ public class Controller implements IFloodlightProviderService, new Object[] { role, sw, Controller.this.role} ); - sw.deliverRoleReply(vendorMessage.getXid(), role); + roleChanger.deliverRoleReply(sw, vendorMessage.getXid(), role); if (sw.isActive()) { // Transition from SLAVE to MASTER. @@ -1060,7 +1060,8 @@ public class Controller implements IFloodlightProviderService, boolean shouldLogError = true; // TODO: should we check that firstRoleReplyReceived is false, // i.e., check only whether the first request fails? - if (sw.checkFirstPendingRoleRequestXid(error.getXid())) { + if (roleChanger.checkFirstPendingRoleRequestXid( + sw, error.getXid())) { boolean isBadVendorError = (error.getErrorType() == OFError.OFErrorType. OFPET_BAD_REQUEST.getValue()); @@ -1081,17 +1082,16 @@ public class Controller implements IFloodlightProviderService, +"role reply earlier", sw); } state.firstRoleReplyReceived = true; - sw.deliverRoleRequestNotSupported(error.getXid()); + roleChanger.deliverRoleRequestNotSupported(sw, error.getXid()); synchronized(roleChanger) { - if (sw.getRole() == null && - Controller.this.role==Role.SLAVE) { + if (Controller.this.role==Role.SLAVE) { // the switch doesn't understand role request // messages and the current controller role is // slave. We need to disconnect the switch. // @see RoleChanger for rationale - sw.getChannel().close(); + sw.disconnectOutputStream(); } - else if (sw.getRole() == null) { + else { // Controller's role is master: add to // active // TODO: check if clearing flow table is @@ -2129,5 +2129,44 @@ public class Controller implements IFloodlightProviderService, switchDescSortedList.add(description); } } + + public void sendNxRoleRequest(IOFSwitch sw, int xid, + Role role, long cookie) throws IOException { + // Convert the role enum to the appropriate integer constant used + // in the NX role request message + int nxRole = 0; + switch (role) { + case EQUAL: + nxRole = OFRoleVendorData.NX_ROLE_OTHER; + break; + case MASTER: + nxRole = OFRoleVendorData.NX_ROLE_MASTER; + break; + case SLAVE: + nxRole = OFRoleVendorData.NX_ROLE_SLAVE; + break; + default: + log.error("Invalid Role specified for switch {}." + + " Disconnecting.", sw); + // TODO: should throw an error + return; + } + + // Construct the role request message + OFVendor roleRequest = (OFVendor)this. + getOFMessageFactory().getMessage(OFType.VENDOR); + roleRequest.setXid(xid); + roleRequest.setVendor(OFNiciraVendorData.NX_VENDOR_ID); + OFRoleRequestVendorData roleRequestData = new OFRoleRequestVendorData(); + roleRequestData.setRole(nxRole); + roleRequest.setVendorData(roleRequestData); + roleRequest.setLengthU(OFVendor.MINIMUM_LENGTH + + roleRequestData.getLength()); + + // Send it to the switch + List<OFMessage> msglist = new ArrayList<OFMessage>(1); + msglist.add(roleRequest); + sw.write(msglist, new FloodlightContext()); + } } diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchImpl.java b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchImpl.java index becbdb561c24260fa6e94af1b03ebf9775375a21..ed972cd691d7e9d920f7ac281afa399f74ee4e36 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchImpl.java +++ b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchImpl.java @@ -24,7 +24,6 @@ import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -41,7 +40,6 @@ import net.floodlightcontroller.core.IOFMessageListener; import net.floodlightcontroller.core.IFloodlightProviderService.Role; import net.floodlightcontroller.core.IOFSwitch; import net.floodlightcontroller.core.annotations.LogMessageDoc; -import net.floodlightcontroller.core.annotations.LogMessageDocs; import net.floodlightcontroller.core.web.serializers.DPIDSerializer; import net.floodlightcontroller.threadpool.IThreadPoolService; import net.floodlightcontroller.util.TimedCache; @@ -58,7 +56,6 @@ import org.openflow.protocol.OFMessage; import org.openflow.protocol.OFPhysicalPort; import org.openflow.protocol.OFPort; import org.openflow.protocol.OFType; -import org.openflow.protocol.OFVendor; import org.openflow.protocol.OFPhysicalPort.OFPortConfig; import org.openflow.protocol.OFPhysicalPort.OFPortState; import org.openflow.protocol.OFStatisticsRequest; @@ -66,9 +63,6 @@ import org.openflow.protocol.statistics.OFDescriptionStatistics; import org.openflow.protocol.statistics.OFStatistics; import org.openflow.util.HexString; import org.openflow.util.U16; -import org.openflow.vendor.nicira.OFNiciraVendorData; -import org.openflow.vendor.nicira.OFRoleRequestVendorData; -import org.openflow.vendor.nicira.OFRoleVendorData; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -80,11 +74,6 @@ public class OFSwitchImpl implements IOFSwitch { // exception that can then be handled by callers? protected static Logger log = LoggerFactory.getLogger(OFSwitchImpl.class); - private static final String HA_CHECK_SWITCH = - "Check the health of the indicated switch. If the problem " + - "persists or occurs repeatedly, it likely indicates a defect " + - "in the switch HA implementation."; - protected ConcurrentMap<Object, Object> attributes; protected IFloodlightProviderService floodlightProvider; protected IThreadPoolService threadPool; @@ -110,17 +99,6 @@ public class OFSwitchImpl implements IOFSwitch { protected TimedCache<Long> timedCache; protected ReentrantReadWriteLock listenerLock; protected ConcurrentMap<Short, Long> portBroadcastCacheHitMap; - /** - * When sending a role request message, the role request is added - * to this queue. If a role reply is received this queue is checked to - * verify that the reply matches the expected reply. We require in order - * delivery of replies. That's why we use a Queue. - * The RoleChanger uses a timeout to ensure we receive a timely reply. - * - * Need to synchronize on this instance if a request is sent, received, - * checked. - */ - protected LinkedList<PendingRoleRequestEntry> pendingRoleRequests; /* Switch features from initial featuresReply */ protected int capabilities; @@ -141,18 +119,6 @@ public class OFSwitchImpl implements IOFSwitch { // for managing our map sizes protected static final int MAX_MACS_PER_SWITCH = 1000; - protected static class PendingRoleRequestEntry { - protected int xid; - protected Role role; - // cookie is used to identify the role "generation". roleChanger uses - protected long cookie; - public PendingRoleRequestEntry(int xid, Role role, long cookie) { - this.xid = xid; - this.role = role; - this.cookie = cookie; - } - } - public OFSwitchImpl() { this.stringId = null; this.attributes = new ConcurrentHashMap<Object, Object>(); @@ -169,7 +135,6 @@ public class OFSwitchImpl implements IOFSwitch { this.timedCache = new TimedCache<Long>(100, 5*1000 ); // 5 seconds interval this.listenerLock = new ReentrantReadWriteLock(); this.portBroadcastCacheHitMap = new ConcurrentHashMap<Short, Long>(); - this.pendingRoleRequests = new LinkedList<OFSwitchImpl.PendingRoleRequestEntry>(); // Defaults properties for an ideal switch this.setAttribute(PROP_FASTWILDCARDS, OFMatch.OFPFW_ALL); @@ -637,177 +602,53 @@ public class OFSwitchImpl implements IOFSwitch { */ public int sendHARoleRequest(Role role, long cookie) throws IOException, HARoleUnsupportedException { + // 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 simply throw an unsupported exception. Boolean supportsNxRole = (Boolean) getAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE); if ((supportsNxRole != null) && !supportsNxRole) { throw new HARoleUnsupportedException(); } - synchronized(pendingRoleRequests) { - // Convert the role enum to the appropriate integer constant used - // in the NX role request message - int nxRole = 0; - switch (role) { - case EQUAL: - nxRole = OFRoleVendorData.NX_ROLE_OTHER; - break; - case MASTER: - nxRole = OFRoleVendorData.NX_ROLE_MASTER; - break; - case SLAVE: - nxRole = OFRoleVendorData.NX_ROLE_SLAVE; - break; - default: - log.error("Invalid Role specified for switch {}." - + " Disconnecting.", this); - // TODO: should throw an error - return 0; - } - - // Construct the role request message - OFVendor roleRequest = (OFVendor)floodlightProvider. - getOFMessageFactory().getMessage(OFType.VENDOR); - int xid = this.getNextTransactionId(); - roleRequest.setXid(xid); - roleRequest.setVendor(OFNiciraVendorData.NX_VENDOR_ID); - OFRoleRequestVendorData roleRequestData = new OFRoleRequestVendorData(); - roleRequestData.setRole(nxRole); - roleRequest.setVendorData(roleRequestData); - roleRequest.setLengthU(OFVendor.MINIMUM_LENGTH + - roleRequestData.getLength()); - - // Send it to the switch - List<OFMessage> msglist = new ArrayList<OFMessage>(1); - msglist.add(roleRequest); - // FIXME: should this use this.write() in order for messages to - // be processed by handleOutgoingMessage() - this.channel.write(msglist); - - pendingRoleRequests.add(new PendingRoleRequestEntry(xid, role, cookie)); - return xid; - } + int xid = this.getNextTransactionId(); + floodlightProvider.sendNxRoleRequest(this, xid, role, cookie); + return xid; } - /** - * Deliver a RoleReply message to this switch. Checks if the reply - * message matches the expected reply (head of the pending request queue). - * We require in-order delivery of replies. If there's any deviation from - * our expectations we disconnect the switch. - * - * We must not check the received role against the controller's current - * role because there's no synchronization but that's fine @see RoleChanger - * - * Will be called by the OFChannelHandler's receive loop - * - * @param xid Xid of the reply message - * @param role The Role in the the reply message - */ - @LogMessageDocs({ - @LogMessageDoc(level="ERROR", - message="Switch {switch}: received unexpected role reply for " + - "Role {role}" + - " Disconnecting switch", - explanation="The switch sent an unexpected HA role reply", - recommendation=HA_CHECK_SWITCH), - @LogMessageDoc(level="ERROR", - message="Switch {switch}: expected role reply with " + - "Xid {xid}, got {xid}. Disconnecting switch", - explanation="The switch sent an unexpected HA role reply", - recommendation=HA_CHECK_SWITCH), - @LogMessageDoc(level="ERROR", - message="Switch {switch}: expected role reply with " + - "Role {role}, got {role}. Disconnecting switch", - explanation="The switch sent an unexpected HA role reply", - recommendation=HA_CHECK_SWITCH) - }) - public void deliverRoleReply(int xid, Role role) { - synchronized(pendingRoleRequests) { - PendingRoleRequestEntry head = pendingRoleRequests.poll(); - if (head == null) { - // Maybe don't disconnect if the role reply we received is - // for the same role we are already in. - log.error("Switch {}: received unexpected role reply for Role {}" + - " Disconnecting switch", this, role ); - this.channel.close(); - } - else if (head.xid != xid) { - // check xid before role!! - log.error("Switch {}: expected role reply with " + - "Xid {}, got {}. Disconnecting switch", - new Object[] { this, head.xid, xid } ); - this.channel.close(); - } - else if (head.role != role) { - log.error("Switch {}: expected role reply with " + - "Role {}, got {}. Disconnecting switch", - new Object[] { this, head.role, role } ); - this.channel.close(); - } - else { - log.debug("Received role reply message from {}, setting role to {}", - this, role); - if (this.role == null && getAttribute(SWITCH_SUPPORTS_NX_ROLE) == null) { - // The first role reply we received. Set the attribute - // that the switch supports roles - setAttribute(SWITCH_SUPPORTS_NX_ROLE, true); - } - this.role = role; - } - } - } - - /** - * Checks whether the given xid matches the xid of the first pending - * role request. - * @param xid - * @return - */ - public boolean checkFirstPendingRoleRequestXid (int xid) { - synchronized(pendingRoleRequests) { - PendingRoleRequestEntry head = pendingRoleRequests.peek(); - if (head == null) - return false; - else - return head.xid == xid; - } - } - - /** - * Checks whether the given request cookie matches the cookie of the first - * pending request - * @param cookie - * @return - */ - public boolean checkFirstPendingRoleRequestCookie(long cookie) { - synchronized(pendingRoleRequests) { - PendingRoleRequestEntry head = pendingRoleRequests.peek(); - if (head == null) - return false; - else - return head.cookie == cookie; + public void setHARole(Role role, boolean replyReceived) { + /* null role implies disconnect the switch */ + if (role == null) { + this.channel.close(); + return; } - } - - /** - * Called if we receive a vendor error message indicating that roles - * are not supported by the switch. If the xid matches the first pending - * one, we'll mark the switch as not supporting roles and remove the head. - * Otherwise we ignore it. - * @param xid - */ - public void deliverRoleRequestNotSupported(int xid) { - synchronized(pendingRoleRequests) { - PendingRoleRequestEntry head = pendingRoleRequests.poll(); - this.role = null; - if (head!=null && head.xid == xid) { - setAttribute(SWITCH_SUPPORTS_NX_ROLE, false); - } - else { - this.channel.close(); - } + + if (this.role == null && getAttribute(SWITCH_SUPPORTS_NX_ROLE) == null) + { + // The first role reply we received. Set the attribute + // that the switch supports roles + setAttribute(SWITCH_SUPPORTS_NX_ROLE, replyReceived); } + this.role = role; } - + @Override public Future<OFFeaturesReply> querySwitchFeaturesReply() throws IOException { diff --git a/src/main/java/net/floodlightcontroller/core/internal/RoleChanger.java b/src/main/java/net/floodlightcontroller/core/internal/RoleChanger.java index b7a8ba9bca1557d97cea79d7522c86dae9faa2d7..cf13919c06c55150a5ebd1673594203a6dd768ce 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/RoleChanger.java +++ b/src/main/java/net/floodlightcontroller/core/internal/RoleChanger.java @@ -4,6 +4,8 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; +import java.util.LinkedList; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.DelayQueue; import java.util.concurrent.Delayed; import java.util.concurrent.TimeUnit; @@ -15,6 +17,7 @@ import net.floodlightcontroller.core.HARoleUnsupportedException; import net.floodlightcontroller.core.IFloodlightProviderService.Role; import net.floodlightcontroller.core.IOFSwitch; import net.floodlightcontroller.core.annotations.LogMessageDoc; +import net.floodlightcontroller.core.annotations.LogMessageDocs; /** * This class handles sending of RoleRequest messages to all connected switches. @@ -120,8 +123,16 @@ public class RoleChanger { protected long lastSubmitTime; protected Thread workerThread; protected long timeout; + protected ConcurrentHashMap<IOFSwitch, LinkedList<PendingRoleRequestEntry>> + pendingRequestMap; + protected static long DEFAULT_TIMEOUT = 15L*1000*1000*1000L; // 15s protected static Logger log = LoggerFactory.getLogger(RoleChanger.class); + private static final String HA_CHECK_SWITCH = + "Check the health of the indicated switch. If the problem " + + "persists or occurs repeatedly, it likely indicates a defect " + + "in the switch HA implementation."; + /** * A queued task to be handled by the Role changer thread. */ @@ -157,6 +168,22 @@ public class RoleChanger { } } + /** + * Per-switch list of pending HA role requests. + * @author shudongz + */ + protected static class PendingRoleRequestEntry { + protected int xid; + protected Role role; + // cookie is used to identify the role "generation". roleChanger uses + protected long cookie; + public PendingRoleRequestEntry(int xid, Role role, long cookie) { + this.xid = xid; + this.role = role; + this.cookie = cookie; + } + } + @LogMessageDoc(level="ERROR", message="RoleRequestWorker task had an uncaught exception.", explanation="An unknown occured while processing an HA " + @@ -203,6 +230,8 @@ public class RoleChanger { } public RoleChanger() { + this.pendingRequestMap = new ConcurrentHashMap<IOFSwitch, + LinkedList<PendingRoleRequestEntry>>(); this.pendingTasks = new DelayQueue<RoleChangeTask>(); this.workerThread = new Thread(new RoleRequestWorker()); this.timeout = DEFAULT_TIMEOUT; @@ -224,12 +253,10 @@ public class RoleChanger { } /** - * Send a role request message to switches. This checks the capabilities - * of the switch for understanding role request messaging. Currently we only - * support the OVS-style role request message, but once the controller - * supports OF 1.2, this function will also handle sending out the - * OF 1.2-style role request message. - * @param switches the collection of switches to send the request too + * Send a role request message to switches. The sw implementation throws + * HARoleUnsupportedException if HA is not supported. Otherwise, it + * returns the transaction id of the request message. + * @param switches the collection of switches to send the request too * @param role the role to request */ @LogMessageDoc(level="WARN", @@ -240,39 +267,23 @@ public class RoleChanger { recommendation=LogMessageDoc.CHECK_SWITCH) protected void sendRoleRequest(Collection<IOFSwitch> switches, Role role, long cookie) { - // 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. Iterator<IOFSwitch> iter = switches.iterator(); while(iter.hasNext()) { IOFSwitch sw = iter.next(); try { - sw.sendHARoleRequest(role, cookie); + int xid = sw.sendHARoleRequest(role, cookie); + PendingRoleRequestEntry entry = + new PendingRoleRequestEntry(xid, role, cookie); + LinkedList<PendingRoleRequestEntry> pendingList + = pendingRequestMap.get(sw); + if (pendingList == null) { + pendingList = new LinkedList<PendingRoleRequestEntry>(); + pendingRequestMap.put(sw, pendingList); + } + // Need to synchronize against removal from list + synchronized(pendingList) { + pendingList.add(entry); + } } catch (IOException e) { log.warn("Failed to send role request message " + "to switch {}: {}. Disconnecting", @@ -280,7 +291,7 @@ public class RoleChanger { sw.getChannel().close(); iter.remove(); } catch (HARoleUnsupportedException e) { - // Handle case #3 + // Switch doesn't support HA role, remove if role is slave if (role == Role.SLAVE) { log.debug("Disconnecting switch {} that doesn't support " + "role request messages from a controller that went to SLAVE mode"); @@ -307,11 +318,151 @@ public class RoleChanger { protected void verifyRoleReplyReceived(Collection<IOFSwitch> switches, long cookie) { for (IOFSwitch sw: switches) { - if (sw.checkFirstPendingRoleRequestCookie(cookie)) { - sw.getChannel().close(); + if (checkFirstPendingRoleRequestCookie(sw, cookie)) { + sw.setHARole(null, false); log.warn("Timeout while waiting for role reply from switch {}." + " Disconnecting", sw); } } } + + /** + * Deliver a RoleReply message for a switch. Checks if the reply + * message matches the expected reply (head of the pending request queue). + * We require in-order delivery of replies. If there's any deviation from + * our expectations we disconnect the switch. + * + * We must not check the received role against the controller's current + * role because there's no synchronization but that's fine @see RoleChanger + * + * Will be called by the OFChannelHandler's receive loop + * + * @param xid Xid of the reply message + * @param role The Role in the the reply message + */ + @LogMessageDocs({ + @LogMessageDoc(level="ERROR", + message="Switch {switch}: received unexpected role reply for " + + "Role {role}" + + " Disconnecting switch", + explanation="The switch sent an unexpected HA role reply", + recommendation=HA_CHECK_SWITCH), + @LogMessageDoc(level="ERROR", + message="Switch {switch}: expected role reply with " + + "Xid {xid}, got {xid}. Disconnecting switch", + explanation="The switch sent an unexpected HA role reply", + recommendation=HA_CHECK_SWITCH), + @LogMessageDoc(level="ERROR", + message="Switch {switch}: expected role reply with " + + "Role {role}, got {role}. Disconnecting switch", + explanation="The switch sent an unexpected HA role reply", + recommendation=HA_CHECK_SWITCH) + }) + + public void deliverRoleReply(IOFSwitch sw, int xid, Role role) { + LinkedList<PendingRoleRequestEntry> pendingRoleRequests = + pendingRequestMap.get(sw); + if (pendingRoleRequests == null) { + log.warn("Switch {}: received unexpected role reply for Role {}" + + ", ignored", sw, role ); + return; + } + synchronized(pendingRoleRequests) { + PendingRoleRequestEntry head = pendingRoleRequests.poll(); + if (head == null) { + // Maybe don't disconnect if the role reply we received is + // for the same role we are already in. + log.error("Switch {}: received unexpected role reply for Role {}" + + " Disconnecting switch", sw, role ); + sw.disconnectOutputStream(); + } + else if (head.xid != xid) { + // check xid before role!! + log.error("Switch {}: expected role reply with " + + "Xid {}, got {}. Disconnecting switch", + new Object[] { this, head.xid, xid } ); + sw.disconnectOutputStream(); + } + else if (head.role != role) { + log.error("Switch {}: expected role reply with " + + "Role {}, got {}. Disconnecting switch", + new Object[] { this, head.role, role } ); + sw.disconnectOutputStream(); + } + else { + log.debug("Received role reply message from {}, setting role to {}", + this, role); + sw.setHARole(role, true); + } + } + } + + /** + * Checks whether the given xid matches the xid of the first pending + * role request. + * @param xid + * @return + */ + public boolean checkFirstPendingRoleRequestXid (IOFSwitch sw, int xid) { + LinkedList<PendingRoleRequestEntry> pendingRoleRequests = + pendingRequestMap.get(sw); + if (pendingRoleRequests == null) { + return false; + } + synchronized(pendingRoleRequests) { + PendingRoleRequestEntry head = pendingRoleRequests.peek(); + if (head == null) + return false; + else + return head.xid == xid; + } + } + + /** + * Checks whether the given request cookie matches the cookie of the first + * pending request + * @param cookie + * @return + */ + public boolean checkFirstPendingRoleRequestCookie(IOFSwitch sw, long cookie) + { + LinkedList<PendingRoleRequestEntry> pendingRoleRequests = + pendingRequestMap.get(sw); + if (pendingRoleRequests == null) { + return false; + } + synchronized(pendingRoleRequests) { + PendingRoleRequestEntry head = pendingRoleRequests.peek(); + if (head == null) + return false; + else + return head.cookie == cookie; + } + } + + /** + * Called if we receive a vendor error message indicating that roles + * are not supported by the switch. If the xid matches the first pending + * one, we'll mark the switch as not supporting roles and remove the head. + * Otherwise we ignore it. + * @param xid + */ + public void deliverRoleRequestNotSupported(IOFSwitch sw, int xid) { + LinkedList<PendingRoleRequestEntry> pendingRoleRequests = + pendingRequestMap.get(sw); + if (pendingRoleRequests == null) { + log.warn("Switch {}: received unexpected error for xid {}" + + ", ignored", sw, xid); + return; + } + synchronized(pendingRoleRequests) { + PendingRoleRequestEntry head = pendingRoleRequests.poll(); + if (head != null && head.xid == xid) { + sw.setHARole(head.role, false); + } else { + sw.disconnectOutputStream(); + } + } + } + }