blob: 2d7e2e76f017e835da3a4771f6ebfaeaba5b393b [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07002 * Copyright 2014 Open Networking Laboratory
Thomas Vachuska781d18b2014-10-27 10:31:25 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * 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
Thomas Vachuska781d18b2014-10-27 10:31:25 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
Thomas Vachuska781d18b2014-10-27 10:31:25 -070015 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.openflow.controller.impl;
tom7ef8ff92014-09-17 13:08:06 -070017
alshabibb452fd72015-04-22 20:46:20 -070018import com.google.common.cache.Cache;
19import com.google.common.cache.CacheBuilder;
Brian O'Connorabafb502014-12-02 22:26:20 -080020import org.onosproject.openflow.controller.RoleState;
21import org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver;
22import org.onosproject.openflow.controller.driver.RoleHandler;
23import org.onosproject.openflow.controller.driver.RoleRecvStatus;
24import org.onosproject.openflow.controller.driver.RoleReplyInfo;
25import org.onosproject.openflow.controller.driver.SwitchStateException;
tom7ef8ff92014-09-17 13:08:06 -070026import org.projectfloodlight.openflow.protocol.OFControllerRole;
27import org.projectfloodlight.openflow.protocol.OFErrorMsg;
28import org.projectfloodlight.openflow.protocol.OFErrorType;
29import org.projectfloodlight.openflow.protocol.OFExperimenter;
30import org.projectfloodlight.openflow.protocol.OFFactories;
tom7ef8ff92014-09-17 13:08:06 -070031import org.projectfloodlight.openflow.protocol.OFNiciraControllerRole;
32import org.projectfloodlight.openflow.protocol.OFNiciraControllerRoleReply;
33import org.projectfloodlight.openflow.protocol.OFRoleReply;
34import org.projectfloodlight.openflow.protocol.OFRoleRequest;
35import org.projectfloodlight.openflow.protocol.OFVersion;
36import org.projectfloodlight.openflow.protocol.errormsg.OFBadRequestErrorMsg;
37import org.projectfloodlight.openflow.protocol.errormsg.OFRoleRequestFailedErrorMsg;
38import org.projectfloodlight.openflow.types.U64;
39import org.slf4j.Logger;
40import org.slf4j.LoggerFactory;
41
alshabibb452fd72015-04-22 20:46:20 -070042import java.io.IOException;
43import java.util.concurrent.TimeUnit;
Ayaka Koshibe00ae8632014-10-31 18:22:44 -070044
tom7ef8ff92014-09-17 13:08:06 -070045
46/**
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
50 * if necessary.
tom7ef8ff92014-09-17 13:08:06 -070051 */
52class RoleManager implements RoleHandler {
53 protected static final long NICIRA_EXPERIMENTER = 0x2320;
54
55 private static Logger log = LoggerFactory.getLogger(RoleManager.class);
Ayaka Koshibe00ae8632014-10-31 18:22:44 -070056
Ayaka Koshibe78bcbc12014-11-19 14:28:58 -080057 // The time until cached XID is evicted. Arbitrary for now.
Ayaka Koshibe00ae8632014-10-31 18:22:44 -070058 private final int pendingXidTimeoutSeconds = 60;
59
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)
64 .build();
tom7ef8ff92014-09-17 13:08:06 -070065
66 // the expectation set by the caller for the returned role
67 private RoleRecvStatus expectation;
68 private final OpenFlowSwitchDriver sw;
69
70
71 public RoleManager(OpenFlowSwitchDriver sw) {
tom7ef8ff92014-09-17 13:08:06 -070072 this.expectation = RoleRecvStatus.MATCHED_CURRENT_ROLE;
73 this.sw = sw;
74 }
75
76 /**
77 * Send NX role request message to the switch requesting the specified
78 * role.
79 *
80 * @param role role to request
81 */
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;
85 switch (role) {
86 case MASTER:
87 roleToSend = OFNiciraControllerRole.ROLE_MASTER;
88 break;
89 case SLAVE:
90 case EQUAL:
91 default:
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.warn("Sending Nx Role.SLAVE to switch {}.", sw);
96 }
97 int xid = sw.getNextTransactionId();
98 OFExperimenter roleRequest = OFFactories.getFactory(OFVersion.OF_10)
99 .buildNiciraControllerRoleRequest()
100 .setXid(xid)
101 .setRole(roleToSend)
102 .build();
alshabibb452fd72015-04-22 20:46:20 -0700103 sw.sendRoleRequest(roleRequest);
tom7ef8ff92014-09-17 13:08:06 -0700104 return xid;
105 }
106
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;
110 switch (role) {
111 case EQUAL:
112 roleToSend = OFControllerRole.ROLE_EQUAL;
113 break;
114 case MASTER:
115 roleToSend = OFControllerRole.ROLE_MASTER;
116 break;
117 case SLAVE:
118 roleToSend = OFControllerRole.ROLE_SLAVE;
119 break;
120 default:
121 log.warn("Sending default role.noChange to switch {}."
122 + " Should only be used for queries.", sw);
123 }
124
125 int xid = sw.getNextTransactionId();
126 OFRoleRequest rrm = OFFactories.getFactory(OFVersion.OF_13)
127 .buildRoleRequest()
128 .setRole(roleToSend)
129 .setXid(xid)
130 //FIXME fix below when we actually use generation ids
131 .setGenerationId(U64.ZERO)
132 .build();
alshabibb452fd72015-04-22 20:46:20 -0700133
134 sw.sendRoleRequest(rrm);
tom7ef8ff92014-09-17 13:08:06 -0700135 return xid;
136 }
137
138 @Override
139 public synchronized boolean sendRoleRequest(RoleState role, RoleRecvStatus exp)
140 throws IOException {
141 this.expectation = exp;
142
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,
149 expectation);
150 return false;
151 }
152 // OF1.0 switch with support for NX_ROLE_REQUEST vendor extn.
153 // make Role.EQUAL become Role.SLAVE
Ayaka Koshibe00ae8632014-10-31 18:22:44 -0700154 RoleState roleToSend = (role == RoleState.EQUAL) ? RoleState.SLAVE : role;
155 pendingReplies.put(sendNxRoleRequest(roleToSend), role);
tom7ef8ff92014-09-17 13:08:06 -0700156 } else {
157 // OF1.3 switch, use OFPT_ROLE_REQUEST message
Ayaka Koshibe00ae8632014-10-31 18:22:44 -0700158 pendingReplies.put(sendOF13RoleRequest(role), role);
tom7ef8ff92014-09-17 13:08:06 -0700159 }
160 return true;
161 }
162
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
167 // master
168 if (exp != RoleRecvStatus.MATCHED_SET_ROLE) {
169
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);
176
177 }
178
179 }
180
181
182 @Override
183 public synchronized RoleRecvStatus deliverRoleReply(RoleReplyInfo rri)
184 throws SwitchStateException {
Ayaka Koshibe00ae8632014-10-31 18:22:44 -0700185 int xid = (int) rri.getXid();
186 RoleState receivedRole = rri.getRole();
187 RoleState expectedRole = pendingReplies.getIfPresent(xid);
188
189 if (expectedRole == null) {
tom7ef8ff92014-09-17 13:08:06 -0700190 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.
Ayaka Koshibe00ae8632014-10-31 18:22:44 -0700195 // FIXME: but we do from the caller anyways.
tom7ef8ff92014-09-17 13:08:06 -0700196 log.debug("Received unexpected RoleReply from "
197 + "Switch: {}. "
198 + "Role in reply is same as current role of this "
199 + "controller for this sw. Ignoring ...",
200 sw.getStringId());
201 return RoleRecvStatus.OTHER_EXPECTATION;
202 } else {
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 ... ",
208 sw.getStringId(),
209 rri, currentRole);
210 throw new SwitchStateException(msg);
211 }
212 }
213 log.debug("Received unexpected RoleReply {} from "
214 + "Switch: {}. "
215 + "This controller has no current role for this sw. "
216 + "Ignoring ...", new Object[] {rri,
Ray Milkey241b96a2014-11-17 13:08:20 -0800217 sw == null ? "(null)" : sw.getStringId(), });
tom7ef8ff92014-09-17 13:08:06 -0700218 return RoleRecvStatus.OTHER_EXPECTATION;
219 }
220
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700221 // XXX Should check generation id meaningfully and other cases of expectations
Ayaka Koshibe00ae8632014-10-31 18:22:44 -0700222 //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;
229 //}
230 sw.returnRoleReply(expectedRole, receivedRole);
tom7ef8ff92014-09-17 13:08:06 -0700231
Ayaka Koshibe00ae8632014-10-31 18:22:44 -0700232 if (expectedRole == receivedRole) {
tom7ef8ff92014-09-17 13:08:06 -0700233 log.debug("Received role reply message from {} that matched "
234 + "expected role-reply {} with expectations {}",
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700235 new Object[] {sw.getStringId(), receivedRole, expectation});
tom7ef8ff92014-09-17 13:08:06 -0700236
Ayaka Koshibe00ae8632014-10-31 18:22:44 -0700237 // Done with this RoleReply; Invalidate
238 pendingReplies.invalidate(xid);
tom7ef8ff92014-09-17 13:08:06 -0700239 if (expectation == RoleRecvStatus.MATCHED_CURRENT_ROLE ||
240 expectation == RoleRecvStatus.MATCHED_SET_ROLE) {
241 return expectation;
242 } else {
243 return RoleRecvStatus.OTHER_EXPECTATION;
244 }
tom7ef8ff92014-09-17 13:08:06 -0700245 }
246
Ayaka Koshibe00ae8632014-10-31 18:22:44 -0700247 pendingReplies.invalidate(xid);
tom7ef8ff92014-09-17 13:08:06 -0700248 // if xids match but role's don't, perhaps its a query (OF1.3)
249 if (expectation == RoleRecvStatus.REPLY_QUERY) {
250 return expectation;
251 }
252
253 return RoleRecvStatus.OTHER_EXPECTATION;
254 }
255
256 /**
257 * Called if we receive an error message. If the xid matches the
258 * pending request we handle it otherwise we ignore it.
259 *
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
262 * to handle
263 */
264 @Override
265 public synchronized RoleRecvStatus deliverError(OFErrorMsg error)
266 throws SwitchStateException {
Ayaka Koshibe00ae8632014-10-31 18:22:44 -0700267 RoleState errorRole = pendingReplies.getIfPresent(error.getXid());
268 if (errorRole == null) {
tom7ef8ff92014-09-17 13:08:06 -0700269 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);
Ayaka Koshibe00ae8632014-10-31 18:22:44 -0700274 } else {
275 log.debug("Received an error msg from sw {}, but no pending "
276 + "requests in role-changer; not handling ...",
277 sw.getStringId());
tom7ef8ff92014-09-17 13:08:06 -0700278 }
279 return RoleRecvStatus.OTHER_EXPECTATION;
280 }
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(),
Ayaka Koshibe00ae8632014-10-31 18:22:44 -0700288 sw.getStringId(), errorRole
tom7ef8ff92014-09-17 13:08:06 -0700289 });
290 return RoleRecvStatus.UNSUPPORTED;
291 }
292
293 if (error.getErrType() == OFErrorType.ROLE_REQUEST_FAILED) {
294 OFRoleRequestFailedErrorMsg rrerr =
295 (OFRoleRequestFailedErrorMsg) error;
296 switch (rrerr.getCode()) {
297 case BAD_ROLE:
298 // switch says that current-role-req has bad role?
299 // for now we disconnect
300 // fall-thru
301 case STALE:
302 // switch says that current-role-req has stale gen-id?
303 // for now we disconnect
304 // fall-thru
305 case UNSUP:
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 ... ",
311 sw.getStringId(),
Ayaka Koshibe00ae8632014-10-31 18:22:44 -0700312 errorRole, rrerr);
tom7ef8ff92014-09-17 13:08:06 -0700313 throw new SwitchStateException(msgx);
314 default:
315 break;
316 }
317 }
318
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;
322 }
323
324 /**
325 * Extract the role from an OFVendor message.
326 *
327 * Extract the role from an OFVendor message if the message is a
328 * Nicira role reply. Otherwise return null.
329 *
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.
335 */
336 @Override
337 public RoleState extractNiciraRoleReply(OFExperimenter experimenterMsg)
338 throws SwitchStateException {
339 int vendor = (int) experimenterMsg.getExperimenter();
340 if (vendor != 0x2320) {
341 return null;
342 }
343 OFNiciraControllerRoleReply nrr =
344 (OFNiciraControllerRoleReply) experimenterMsg;
345
346 RoleState role = null;
347 OFNiciraControllerRole ncr = nrr.getRole();
Madan Jampani08822c42014-11-04 17:17:46 -0800348 switch (ncr) {
tom7ef8ff92014-09-17 13:08:06 -0700349 case ROLE_MASTER:
350 role = RoleState.MASTER;
351 break;
352 case ROLE_OTHER:
353 role = RoleState.EQUAL;
354 break;
355 case ROLE_SLAVE:
356 role = RoleState.SLAVE;
357 break;
358 default: //handled below
359 }
360
361 if (role == null) {
362 String msg = String.format("Switch: [%s], "
363 + "received NX_ROLE_REPLY with invalid role "
364 + "value %s",
365 sw.getStringId(),
366 nrr.getRole());
367 throw new SwitchStateException(msg);
368 }
369 return role;
370 }
371
372 /**
373 * Extract the role information from an OF1.3 Role Reply Message.
374 *
375 * @param rrmsg the role message
376 * @return RoleReplyInfo object
377 * @throws SwitchStateException if the role information could not be extracted.
378 */
379 @Override
380 public RoleReplyInfo extractOFRoleReply(OFRoleReply rrmsg)
381 throws SwitchStateException {
382 OFControllerRole cr = rrmsg.getRole();
383 RoleState role = null;
Madan Jampani08822c42014-11-04 17:17:46 -0800384 switch (cr) {
tom7ef8ff92014-09-17 13:08:06 -0700385 case ROLE_EQUAL:
386 role = RoleState.EQUAL;
387 break;
388 case ROLE_MASTER:
389 role = RoleState.MASTER;
390 break;
391 case ROLE_SLAVE:
392 role = RoleState.SLAVE;
393 break;
394 case ROLE_NOCHANGE: // switch should send current role
395 default:
396 String msg = String.format("Unknown controller role %s "
397 + "received from switch %s", cr, sw);
398 throw new SwitchStateException(msg);
399 }
400
401 return new RoleReplyInfo(role, rrmsg.getGenerationId(), rrmsg.getXid());
402 }
403
404}
405