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. "
216 + "Ignoring ...", new Object[] {rri,
217 sw == null ? "(null)" : sw.getStringId(), });
218 return RoleRecvStatus.OTHER_EXPECTATION;
221 // XXX Should check generation id meaningfully and other cases of expectations
222 //if (pendingXid != xid) {
223 // log.info("Received older role reply from " +
224 // "switch {} ({}). Ignoring. " +
225 // "Waiting for {}, xid={}",
226 // new Object[] {sw.getStringId(), rri,
227 // pendingRole, pendingXid });
228 // return RoleRecvStatus.OLD_REPLY;
230 sw.returnRoleReply(expectedRole, receivedRole);
232 if (expectedRole == receivedRole) {
233 log.debug("Received role reply message from {} that matched "
234 + "expected role-reply {} with expectations {}",
235 new Object[] {sw.getStringId(), receivedRole, expectation});
237 // Done with this RoleReply; Invalidate
238 pendingReplies.invalidate(xid);
239 if (expectation == RoleRecvStatus.MATCHED_CURRENT_ROLE ||
240 expectation == RoleRecvStatus.MATCHED_SET_ROLE) {
243 return RoleRecvStatus.OTHER_EXPECTATION;
247 pendingReplies.invalidate(xid);
248 // if xids match but role's don't, perhaps its a query (OF1.3)
249 if (expectation == RoleRecvStatus.REPLY_QUERY) {
253 return RoleRecvStatus.OTHER_EXPECTATION;
257 * Called if we receive an error message. If the xid matches the
258 * pending request we handle it otherwise we ignore it.
260 * Note: since we only keep the last pending request we might get
261 * error messages for earlier role requests that we won't be able
265 public synchronized RoleRecvStatus deliverError(OFErrorMsg error)
266 throws SwitchStateException {
267 RoleState errorRole = pendingReplies.getIfPresent(error.getXid());
268 if (errorRole == null) {
269 if (error.getErrType() == OFErrorType.ROLE_REQUEST_FAILED) {
270 log.debug("Received an error msg from sw {} for a role request,"
271 + " but not for pending request in role-changer; "
272 + " ignoring error {} ...",
273 sw.getStringId(), error);
275 log.debug("Received an error msg from sw {}, but no pending "
276 + "requests in role-changer; not handling ...",
279 return RoleRecvStatus.OTHER_EXPECTATION;
281 // it is an error related to a currently pending role request message
282 if (error.getErrType() == OFErrorType.BAD_REQUEST) {
283 log.error("Received a error msg {} from sw {} for "
284 + "pending role request {}. Switch driver indicates "
285 + "role-messaging is supported. Possible issues in "
286 + "switch driver configuration?", new Object[] {
287 ((OFBadRequestErrorMsg) error).toString(),
288 sw.getStringId(), errorRole
290 return RoleRecvStatus.UNSUPPORTED;
293 if (error.getErrType() == OFErrorType.ROLE_REQUEST_FAILED) {
294 OFRoleRequestFailedErrorMsg rrerr =
295 (OFRoleRequestFailedErrorMsg) error;
296 switch (rrerr.getCode()) {
298 // switch says that current-role-req has bad role?
299 // for now we disconnect
302 // switch says that current-role-req has stale gen-id?
303 // for now we disconnect
306 // switch says that current-role-req has role that
307 // cannot be supported? for now we disconnect
308 String msgx = String.format("Switch: [%s], "
309 + "received Error to for pending role request [%s]. "
310 + "Error:[%s]. Disconnecting switch ... ",
313 throw new SwitchStateException(msgx);
319 // This error message was for a role request message but we dont know
320 // how to handle errors for nicira role request messages
321 return RoleRecvStatus.OTHER_EXPECTATION;
325 * Extract the role from an OFVendor message.
327 * Extract the role from an OFVendor message if the message is a
328 * Nicira role reply. Otherwise return null.
330 * @param experimenterMsg message
331 * @return The role in the message if the message is a Nicira role
332 * reply, null otherwise.
333 * @throws SwitchStateException If the message is a Nicira role reply
334 * but the numeric role value is unknown.
337 public RoleState extractNiciraRoleReply(OFExperimenter experimenterMsg)
338 throws SwitchStateException {
339 int vendor = (int) experimenterMsg.getExperimenter();
340 if (vendor != 0x2320) {
343 OFNiciraControllerRoleReply nrr =
344 (OFNiciraControllerRoleReply) experimenterMsg;
346 RoleState role = null;
347 OFNiciraControllerRole ncr = nrr.getRole();
350 role = RoleState.MASTER;
353 role = RoleState.EQUAL;
356 role = RoleState.SLAVE;
358 default: //handled below
362 String msg = String.format("Switch: [%s], "
363 + "received NX_ROLE_REPLY with invalid role "
367 throw new SwitchStateException(msg);
373 * Extract the role information from an OF1.3 Role Reply Message.
375 * @param rrmsg the role message
376 * @return RoleReplyInfo object
377 * @throws SwitchStateException if the role information could not be extracted.
380 public RoleReplyInfo extractOFRoleReply(OFRoleReply rrmsg)
381 throws SwitchStateException {
382 OFControllerRole cr = rrmsg.getRole();
383 RoleState role = null;
386 role = RoleState.EQUAL;
389 role = RoleState.MASTER;
392 role = RoleState.SLAVE;
394 case ROLE_NOCHANGE: // switch should send current role
396 String msg = String.format("Unknown controller role %s "
397 + "received from switch %s", cr, sw);
398 throw new SwitchStateException(msg);
401 return new RoleReplyInfo(role, rrmsg.getGenerationId(), rrmsg.getXid());