blob: f3e843738a1ef688ac6fafd4882b9f9c054470e0 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
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
Carmelo Casconeef478a62019-01-29 18:45:22 -080061 private Cache<Long, RoleState> pendingReplies =
Ayaka Koshibe00ae8632014-10-31 18:22:44 -070062 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 */
Carmelo Casconeef478a62019-01-29 18:45:22 -080082 private long sendNxRoleRequest(RoleState role) throws IOException {
tom7ef8ff92014-09-17 13:08:06 -070083 // 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;
Thomas Vachuska3358af22015-05-19 18:40:34 -070095 log.debug("Sending Nx Role.SLAVE to switch {}.", sw);
tom7ef8ff92014-09-17 13:08:06 -070096 }
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
Carmelo Casconeef478a62019-01-29 18:45:22 -0800107 private long sendOF13RoleRequest(RoleState role) throws IOException {
tom7ef8ff92014-09-17 13:08:06 -0700108 // 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();
Yuta HIGUCHI6512f3e2017-05-18 17:21:24 -0700126 OFRoleRequest rrm = sw.factory()
tom7ef8ff92014-09-17 13:08:06 -0700127 .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. "
Ray Milkey6bc43c22015-11-06 13:22:38 -0800216 + "Ignoring ...",
217 rri,
218 sw == null ? "(null)" : sw.getStringId());
tom7ef8ff92014-09-17 13:08:06 -0700219 return RoleRecvStatus.OTHER_EXPECTATION;
220 }
221
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700222 // XXX Should check generation id meaningfully and other cases of expectations
Ayaka Koshibe00ae8632014-10-31 18:22:44 -0700223 //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;
230 //}
231 sw.returnRoleReply(expectedRole, receivedRole);
tom7ef8ff92014-09-17 13:08:06 -0700232
Ayaka Koshibe00ae8632014-10-31 18:22:44 -0700233 if (expectedRole == receivedRole) {
tom7ef8ff92014-09-17 13:08:06 -0700234 log.debug("Received role reply message from {} that matched "
235 + "expected role-reply {} with expectations {}",
Ray Milkey6bc43c22015-11-06 13:22:38 -0800236 sw.getStringId(), receivedRole, expectation);
tom7ef8ff92014-09-17 13:08:06 -0700237
Ayaka Koshibe00ae8632014-10-31 18:22:44 -0700238 // Done with this RoleReply; Invalidate
239 pendingReplies.invalidate(xid);
tom7ef8ff92014-09-17 13:08:06 -0700240 if (expectation == RoleRecvStatus.MATCHED_CURRENT_ROLE ||
241 expectation == RoleRecvStatus.MATCHED_SET_ROLE) {
242 return expectation;
243 } else {
244 return RoleRecvStatus.OTHER_EXPECTATION;
245 }
tom7ef8ff92014-09-17 13:08:06 -0700246 }
247
Ayaka Koshibe00ae8632014-10-31 18:22:44 -0700248 pendingReplies.invalidate(xid);
tom7ef8ff92014-09-17 13:08:06 -0700249 // if xids match but role's don't, perhaps its a query (OF1.3)
250 if (expectation == RoleRecvStatus.REPLY_QUERY) {
251 return expectation;
252 }
253
254 return RoleRecvStatus.OTHER_EXPECTATION;
255 }
256
257 /**
258 * Called if we receive an error message. If the xid matches the
259 * pending request we handle it otherwise we ignore it.
260 *
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
263 * to handle
264 */
265 @Override
266 public synchronized RoleRecvStatus deliverError(OFErrorMsg error)
267 throws SwitchStateException {
Ayaka Koshibe00ae8632014-10-31 18:22:44 -0700268 RoleState errorRole = pendingReplies.getIfPresent(error.getXid());
269 if (errorRole == null) {
tom7ef8ff92014-09-17 13:08:06 -0700270 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);
Ayaka Koshibe00ae8632014-10-31 18:22:44 -0700275 } else {
276 log.debug("Received an error msg from sw {}, but no pending "
277 + "requests in role-changer; not handling ...",
278 sw.getStringId());
tom7ef8ff92014-09-17 13:08:06 -0700279 }
280 return RoleRecvStatus.OTHER_EXPECTATION;
281 }
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 "
Ray Milkey6bc43c22015-11-06 13:22:38 -0800287 + "switch driver configuration?",
288 ((OFBadRequestErrorMsg) error).toString(),
289 sw.getStringId(),
290 errorRole);
tom7ef8ff92014-09-17 13:08:06 -0700291 return RoleRecvStatus.UNSUPPORTED;
292 }
293
294 if (error.getErrType() == OFErrorType.ROLE_REQUEST_FAILED) {
295 OFRoleRequestFailedErrorMsg rrerr =
296 (OFRoleRequestFailedErrorMsg) error;
297 switch (rrerr.getCode()) {
298 case BAD_ROLE:
299 // switch says that current-role-req has bad role?
300 // for now we disconnect
301 // fall-thru
302 case STALE:
303 // switch says that current-role-req has stale gen-id?
304 // for now we disconnect
305 // fall-thru
306 case UNSUP:
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 ... ",
312 sw.getStringId(),
Ayaka Koshibe00ae8632014-10-31 18:22:44 -0700313 errorRole, rrerr);
tom7ef8ff92014-09-17 13:08:06 -0700314 throw new SwitchStateException(msgx);
315 default:
316 break;
317 }
318 }
319
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;
323 }
324
325 /**
326 * Extract the role from an OFVendor message.
327 *
328 * Extract the role from an OFVendor message if the message is a
329 * Nicira role reply. Otherwise return null.
330 *
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.
336 */
337 @Override
338 public RoleState extractNiciraRoleReply(OFExperimenter experimenterMsg)
339 throws SwitchStateException {
340 int vendor = (int) experimenterMsg.getExperimenter();
341 if (vendor != 0x2320) {
342 return null;
343 }
344 OFNiciraControllerRoleReply nrr =
345 (OFNiciraControllerRoleReply) experimenterMsg;
346
347 RoleState role = null;
348 OFNiciraControllerRole ncr = nrr.getRole();
Madan Jampani08822c42014-11-04 17:17:46 -0800349 switch (ncr) {
tom7ef8ff92014-09-17 13:08:06 -0700350 case ROLE_MASTER:
351 role = RoleState.MASTER;
352 break;
353 case ROLE_OTHER:
354 role = RoleState.EQUAL;
355 break;
356 case ROLE_SLAVE:
357 role = RoleState.SLAVE;
358 break;
359 default: //handled below
360 }
361
362 if (role == null) {
363 String msg = String.format("Switch: [%s], "
364 + "received NX_ROLE_REPLY with invalid role "
365 + "value %s",
366 sw.getStringId(),
367 nrr.getRole());
368 throw new SwitchStateException(msg);
369 }
370 return role;
371 }
372
373 /**
374 * Extract the role information from an OF1.3 Role Reply Message.
375 *
376 * @param rrmsg the role message
377 * @return RoleReplyInfo object
378 * @throws SwitchStateException if the role information could not be extracted.
379 */
380 @Override
381 public RoleReplyInfo extractOFRoleReply(OFRoleReply rrmsg)
382 throws SwitchStateException {
383 OFControllerRole cr = rrmsg.getRole();
384 RoleState role = null;
Madan Jampani08822c42014-11-04 17:17:46 -0800385 switch (cr) {
tom7ef8ff92014-09-17 13:08:06 -0700386 case ROLE_EQUAL:
387 role = RoleState.EQUAL;
388 break;
389 case ROLE_MASTER:
390 role = RoleState.MASTER;
391 break;
392 case ROLE_SLAVE:
393 role = RoleState.SLAVE;
394 break;
395 case ROLE_NOCHANGE: // switch should send current role
396 default:
397 String msg = String.format("Unknown controller role %s "
398 + "received from switch %s", cr, sw);
399 throw new SwitchStateException(msg);
400 }
401
402 return new RoleReplyInfo(role, rrmsg.getGenerationId(), rrmsg.getXid());
403 }
404
405}
406