2  * Copyright 2014 Open Networking Laboratory
 
   4  * Licensed under the Apache License, Version 2.0 (the "License");
 
   5  * you may not use this file except in compliance with the License.
 
   6  * You may obtain a copy of the License at
 
   8  *     http://www.apache.org/licenses/LICENSE-2.0
 
  10  * Unless required by applicable law or agreed to in writing, software
 
  11  * distributed under the License is distributed on an "AS IS" BASIS,
 
  12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
  13  * See the License for the specific language governing permissions and
 
  14  * limitations under the License.
 
  16 package org.onosproject.openflow.controller.impl;
 
  18 import com.google.common.cache.Cache;
 
  19 import com.google.common.cache.CacheBuilder;
 
  20 import org.onosproject.openflow.controller.RoleState;
 
  21 import org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver;
 
  22 import org.onosproject.openflow.controller.driver.RoleHandler;
 
  23 import org.onosproject.openflow.controller.driver.RoleRecvStatus;
 
  24 import org.onosproject.openflow.controller.driver.RoleReplyInfo;
 
  25 import org.onosproject.openflow.controller.driver.SwitchStateException;
 
  26 import org.projectfloodlight.openflow.protocol.OFControllerRole;
 
  27 import org.projectfloodlight.openflow.protocol.OFErrorMsg;
 
  28 import org.projectfloodlight.openflow.protocol.OFErrorType;
 
  29 import org.projectfloodlight.openflow.protocol.OFExperimenter;
 
  30 import org.projectfloodlight.openflow.protocol.OFFactories;
 
  31 import org.projectfloodlight.openflow.protocol.OFNiciraControllerRole;
 
  32 import org.projectfloodlight.openflow.protocol.OFNiciraControllerRoleReply;
 
  33 import org.projectfloodlight.openflow.protocol.OFRoleReply;
 
  34 import org.projectfloodlight.openflow.protocol.OFRoleRequest;
 
  35 import org.projectfloodlight.openflow.protocol.OFVersion;
 
  36 import org.projectfloodlight.openflow.protocol.errormsg.OFBadRequestErrorMsg;
 
  37 import org.projectfloodlight.openflow.protocol.errormsg.OFRoleRequestFailedErrorMsg;
 
  38 import org.projectfloodlight.openflow.types.U64;
 
  39 import org.slf4j.Logger;
 
  40 import org.slf4j.LoggerFactory;
 
  42 import java.io.IOException;
 
  43 import java.util.concurrent.TimeUnit;
 
  47  * A utility class to handle role requests and replies for this channel.
 
  48  * After a role request is submitted the role changer keeps track of the
 
  49  * pending request, collects the reply (if any) and times out the request
 
  52 class RoleManager implements RoleHandler {
 
  53     protected static final long NICIRA_EXPERIMENTER = 0x2320;
 
  55     private static Logger log = LoggerFactory.getLogger(RoleManager.class);
 
  57     // The time until cached XID is evicted. Arbitrary for now.
 
  58     private final int pendingXidTimeoutSeconds = 60;
 
  60     // The cache for pending expected RoleReplies keyed on expected XID
 
  61     private Cache<Integer, RoleState> pendingReplies =
 
  62             CacheBuilder.newBuilder()
 
  63                 .expireAfterWrite(pendingXidTimeoutSeconds, TimeUnit.SECONDS)
 
  66     // the expectation set by the caller for the returned role
 
  67     private RoleRecvStatus expectation;
 
  68     private final OpenFlowSwitchDriver sw;
 
  71     public RoleManager(OpenFlowSwitchDriver sw) {
 
  72         this.expectation = RoleRecvStatus.MATCHED_CURRENT_ROLE;
 
  77      * Send NX role request message to the switch requesting the specified
 
  80      * @param role role to request
 
  82     private int sendNxRoleRequest(RoleState role) throws IOException {
 
  83         // Convert the role enum to the appropriate role to send
 
  84         OFNiciraControllerRole roleToSend = OFNiciraControllerRole.ROLE_OTHER;
 
  87             roleToSend = OFNiciraControllerRole.ROLE_MASTER;
 
  92             // ensuring that the only two roles sent to 1.0 switches with
 
  93             // Nicira role support, are MASTER and SLAVE
 
  94             roleToSend = OFNiciraControllerRole.ROLE_OTHER;
 
  95             log.debug("Sending Nx Role.SLAVE to switch {}.", sw);
 
  97         int xid = sw.getNextTransactionId();
 
  98         OFExperimenter roleRequest = OFFactories.getFactory(OFVersion.OF_10)
 
  99                 .buildNiciraControllerRoleRequest()
 
 103         sw.sendRoleRequest(roleRequest);
 
 107     private int sendOF13RoleRequest(RoleState role) throws IOException {
 
 108         // Convert the role enum to the appropriate role to send
 
 109         OFControllerRole roleToSend = OFControllerRole.ROLE_NOCHANGE;
 
 112             roleToSend = OFControllerRole.ROLE_EQUAL;
 
 115             roleToSend = OFControllerRole.ROLE_MASTER;
 
 118             roleToSend = OFControllerRole.ROLE_SLAVE;
 
 121             log.warn("Sending default role.noChange to switch {}."
 
 122                     + " Should only be used for queries.", sw);
 
 125         int xid = sw.getNextTransactionId();
 
 126         OFRoleRequest rrm = OFFactories.getFactory(OFVersion.OF_13)
 
 130                 //FIXME fix below when we actually use generation ids
 
 131                 .setGenerationId(U64.ZERO)
 
 134         sw.sendRoleRequest(rrm);
 
 139     public synchronized boolean sendRoleRequest(RoleState role, RoleRecvStatus exp)
 
 141         this.expectation = exp;
 
 143         if (sw.factory().getVersion() == OFVersion.OF_10) {
 
 144             Boolean supportsNxRole = sw.supportNxRole();
 
 145             if (!supportsNxRole) {
 
 146                 log.debug("Switch driver indicates no support for Nicira "
 
 147                         + "role request messages. Not sending ...");
 
 148                 handleUnsentRoleMessage(role,
 
 152             // OF1.0 switch with support for NX_ROLE_REQUEST vendor extn.
 
 153             // make Role.EQUAL become Role.SLAVE
 
 154             RoleState roleToSend = (role == RoleState.EQUAL) ? RoleState.SLAVE : role;
 
 155             pendingReplies.put(sendNxRoleRequest(roleToSend), role);
 
 157             // OF1.3 switch, use OFPT_ROLE_REQUEST message
 
 158             pendingReplies.put(sendOF13RoleRequest(role), role);
 
 163     private void handleUnsentRoleMessage(RoleState role,
 
 164             RoleRecvStatus exp) throws IOException {
 
 165         // typically this is triggered for a switch where role messages
 
 166         // are not supported - we confirm that the role being set is
 
 168         if (exp != RoleRecvStatus.MATCHED_SET_ROLE) {
 
 170             log.error("Expected MASTER role from registry for switch "
 
 171                     + "which has no support for role-messages."
 
 172                     + "Received {}. It is possible that this switch "
 
 173                     + "is connected to other controllers, in which "
 
 174                     + "case it should support role messages - not "
 
 175                     + "moving forward.", role);
 
 183     public synchronized RoleRecvStatus deliverRoleReply(RoleReplyInfo rri)
 
 184             throws SwitchStateException {
 
 185         int xid = (int) rri.getXid();
 
 186         RoleState receivedRole = rri.getRole();
 
 187         RoleState expectedRole = pendingReplies.getIfPresent(xid);
 
 189         if (expectedRole == null) {
 
 190             RoleState currentRole = (sw != null) ? sw.getRole() : null;
 
 191             if (currentRole != null) {
 
 192                 if (currentRole == rri.getRole()) {
 
 193                     // Don't disconnect if the role reply we received is
 
 194                     // for the same role we are already in.
 
 195                     // FIXME: but we do from the caller anyways.
 
 196                     log.debug("Received unexpected RoleReply from "
 
 198                             + "Role in reply is same as current role of this "
 
 199                             + "controller for this sw. Ignoring ...",
 
 201                     return RoleRecvStatus.OTHER_EXPECTATION;
 
 203                     String msg = String.format("Switch: [%s], "
 
 204                             + "received unexpected RoleReply[%s]. "
 
 205                             + "No roles are pending, and this controller's "
 
 206                             + "current role:[%s] does not match reply. "
 
 207                             + "Disconnecting switch ... ",
 
 210                     throw new SwitchStateException(msg);
 
 213             log.debug("Received unexpected RoleReply {} from "
 
 215                     + "This controller has no current role for this sw. "
 
 218                       sw == null ? "(null)" : sw.getStringId());
 
 219             return RoleRecvStatus.OTHER_EXPECTATION;
 
 222         // XXX Should check generation id meaningfully and other cases of expectations
 
 223         //if (pendingXid != xid) {
 
 224         //    log.info("Received older role reply from " +
 
 225         //            "switch {} ({}). Ignoring. " +
 
 226         //            "Waiting for {}, xid={}",
 
 227         //            new Object[] {sw.getStringId(), rri,
 
 228         //            pendingRole, pendingXid });
 
 229         //    return RoleRecvStatus.OLD_REPLY;
 
 231         sw.returnRoleReply(expectedRole, receivedRole);
 
 233         if (expectedRole == receivedRole) {
 
 234             log.debug("Received role reply message from {} that matched "
 
 235                     + "expected role-reply {} with expectations {}",
 
 236                     sw.getStringId(), receivedRole, expectation);
 
 238             // Done with this RoleReply; Invalidate
 
 239             pendingReplies.invalidate(xid);
 
 240             if (expectation == RoleRecvStatus.MATCHED_CURRENT_ROLE ||
 
 241                     expectation == RoleRecvStatus.MATCHED_SET_ROLE) {
 
 244                 return RoleRecvStatus.OTHER_EXPECTATION;
 
 248         pendingReplies.invalidate(xid);
 
 249         // if xids match but role's don't, perhaps its a query (OF1.3)
 
 250         if (expectation == RoleRecvStatus.REPLY_QUERY) {
 
 254         return RoleRecvStatus.OTHER_EXPECTATION;
 
 258      * Called if we receive an  error message. If the xid matches the
 
 259      * pending request we handle it otherwise we ignore it.
 
 261      * Note: since we only keep the last pending request we might get
 
 262      * error messages for earlier role requests that we won't be able
 
 266     public synchronized RoleRecvStatus deliverError(OFErrorMsg error)
 
 267             throws SwitchStateException {
 
 268         RoleState errorRole = pendingReplies.getIfPresent(error.getXid());
 
 269         if (errorRole == null) {
 
 270             if (error.getErrType() == OFErrorType.ROLE_REQUEST_FAILED) {
 
 271                 log.debug("Received an error msg from sw {} for a role request,"
 
 272                         + " but not for pending request in role-changer; "
 
 273                         + " ignoring error {} ...",
 
 274                         sw.getStringId(), error);
 
 276                 log.debug("Received an error msg from sw {}, but no pending "
 
 277                         + "requests in role-changer; not handling ...",
 
 280             return RoleRecvStatus.OTHER_EXPECTATION;
 
 282         // it is an error related to a currently pending role request message
 
 283         if (error.getErrType() == OFErrorType.BAD_REQUEST) {
 
 284             log.error("Received a error msg {} from sw {} for "
 
 285                     + "pending role request {}. Switch driver indicates "
 
 286                     + "role-messaging is supported. Possible issues in "
 
 287                     + "switch driver configuration?",
 
 288                     ((OFBadRequestErrorMsg) error).toString(),
 
 291             return RoleRecvStatus.UNSUPPORTED;
 
 294         if (error.getErrType() == OFErrorType.ROLE_REQUEST_FAILED) {
 
 295             OFRoleRequestFailedErrorMsg rrerr =
 
 296                     (OFRoleRequestFailedErrorMsg) error;
 
 297             switch (rrerr.getCode()) {
 
 299                 // switch says that current-role-req has bad role?
 
 300                 // for now we disconnect
 
 303                 // switch says that current-role-req has stale gen-id?
 
 304                 // for now we disconnect
 
 307                 // switch says that current-role-req has role that
 
 308                 // cannot be supported? for now we disconnect
 
 309                 String msgx = String.format("Switch: [%s], "
 
 310                         + "received Error to for pending role request [%s]. "
 
 311                         + "Error:[%s]. Disconnecting switch ... ",
 
 314                 throw new SwitchStateException(msgx);
 
 320         // This error message was for a role request message but we dont know
 
 321         // how to handle errors for nicira role request messages
 
 322         return RoleRecvStatus.OTHER_EXPECTATION;
 
 326      * Extract the role from an OFVendor message.
 
 328      * Extract the role from an OFVendor message if the message is a
 
 329      * Nicira role reply. Otherwise return null.
 
 331      * @param experimenterMsg message
 
 332      * @return The role in the message if the message is a Nicira role
 
 333      * reply, null otherwise.
 
 334      * @throws SwitchStateException If the message is a Nicira role reply
 
 335      * but the numeric role value is unknown.
 
 338     public RoleState extractNiciraRoleReply(OFExperimenter experimenterMsg)
 
 339             throws SwitchStateException {
 
 340         int vendor = (int) experimenterMsg.getExperimenter();
 
 341         if (vendor != 0x2320) {
 
 344         OFNiciraControllerRoleReply nrr =
 
 345                 (OFNiciraControllerRoleReply) experimenterMsg;
 
 347         RoleState role = null;
 
 348         OFNiciraControllerRole ncr = nrr.getRole();
 
 351             role = RoleState.MASTER;
 
 354             role = RoleState.EQUAL;
 
 357             role = RoleState.SLAVE;
 
 359         default: //handled below
 
 363             String msg = String.format("Switch: [%s], "
 
 364                     + "received NX_ROLE_REPLY with invalid role "
 
 368             throw new SwitchStateException(msg);
 
 374      * Extract the role information from an OF1.3 Role Reply Message.
 
 376      * @param rrmsg the role message
 
 377      * @return RoleReplyInfo object
 
 378      * @throws SwitchStateException if the role information could not be extracted.
 
 381     public RoleReplyInfo extractOFRoleReply(OFRoleReply rrmsg)
 
 382             throws SwitchStateException {
 
 383         OFControllerRole cr = rrmsg.getRole();
 
 384         RoleState role = null;
 
 387             role = RoleState.EQUAL;
 
 390             role = RoleState.MASTER;
 
 393             role = RoleState.SLAVE;
 
 395         case ROLE_NOCHANGE: // switch should send current role
 
 397             String msg = String.format("Unknown controller role %s "
 
 398                     + "received from switch %s", cr, sw);
 
 399             throw new SwitchStateException(msg);
 
 402         return new RoleReplyInfo(role, rrmsg.getGenerationId(), rrmsg.getXid());