blob: c9f71b78987e569cb85bf72c63e140aa1bce5690 [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 */
tom9c94c5b2014-09-17 13:14:42 -070016package org.onlab.onos.openflow.controller.impl;
tom7ef8ff92014-09-17 13:08:06 -070017
18import java.io.IOException;
19import java.util.Collections;
20
tom9c94c5b2014-09-17 13:14:42 -070021import org.onlab.onos.openflow.controller.RoleState;
22import org.onlab.onos.openflow.controller.driver.OpenFlowSwitchDriver;
23import org.onlab.onos.openflow.controller.driver.RoleHandler;
24import org.onlab.onos.openflow.controller.driver.RoleRecvStatus;
25import org.onlab.onos.openflow.controller.driver.RoleReplyInfo;
26import org.onlab.onos.openflow.controller.driver.SwitchStateException;
tom7ef8ff92014-09-17 13:08:06 -070027import org.projectfloodlight.openflow.protocol.OFControllerRole;
28import org.projectfloodlight.openflow.protocol.OFErrorMsg;
29import org.projectfloodlight.openflow.protocol.OFErrorType;
30import org.projectfloodlight.openflow.protocol.OFExperimenter;
31import org.projectfloodlight.openflow.protocol.OFFactories;
32import org.projectfloodlight.openflow.protocol.OFMessage;
33import org.projectfloodlight.openflow.protocol.OFNiciraControllerRole;
34import org.projectfloodlight.openflow.protocol.OFNiciraControllerRoleReply;
35import org.projectfloodlight.openflow.protocol.OFRoleReply;
36import org.projectfloodlight.openflow.protocol.OFRoleRequest;
37import org.projectfloodlight.openflow.protocol.OFVersion;
38import org.projectfloodlight.openflow.protocol.errormsg.OFBadRequestErrorMsg;
39import org.projectfloodlight.openflow.protocol.errormsg.OFRoleRequestFailedErrorMsg;
40import org.projectfloodlight.openflow.types.U64;
41import org.slf4j.Logger;
42import org.slf4j.LoggerFactory;
43
44
45/**
46 * A utility class to handle role requests and replies for this channel.
47 * After a role request is submitted the role changer keeps track of the
48 * pending request, collects the reply (if any) and times out the request
49 * if necessary.
50 *
51 * To simplify role handling we only keep track of the /last/ pending
52 * role reply send to the switch. If multiple requests are pending and
53 * we receive replies for earlier requests we ignore them. However, this
54 * way of handling pending requests implies that we could wait forever if
55 * a new request is submitted before the timeout triggers. If necessary
56 * we could work around that though.
57 */
58class RoleManager implements RoleHandler {
59 protected static final long NICIRA_EXPERIMENTER = 0x2320;
60
61 private static Logger log = LoggerFactory.getLogger(RoleManager.class);
62 // indicates that a request is currently pending
63 // needs to be volatile to allow correct double-check idiom
64 private volatile boolean requestPending;
65 // the transaction Id of the pending request
66 private int pendingXid;
67 // the role that's pending
68 private RoleState pendingRole;
69
70 // the expectation set by the caller for the returned role
71 private RoleRecvStatus expectation;
72 private final OpenFlowSwitchDriver sw;
73
74
75 public RoleManager(OpenFlowSwitchDriver sw) {
76 this.requestPending = false;
77 this.pendingXid = -1;
78 this.pendingRole = null;
79 this.expectation = RoleRecvStatus.MATCHED_CURRENT_ROLE;
80 this.sw = sw;
81 }
82
83 /**
84 * Send NX role request message to the switch requesting the specified
85 * role.
86 *
87 * @param role role to request
88 */
89 private int sendNxRoleRequest(RoleState role) throws IOException {
90 // Convert the role enum to the appropriate role to send
91 OFNiciraControllerRole roleToSend = OFNiciraControllerRole.ROLE_OTHER;
92 switch (role) {
93 case MASTER:
94 roleToSend = OFNiciraControllerRole.ROLE_MASTER;
95 break;
96 case SLAVE:
97 case EQUAL:
98 default:
99 // ensuring that the only two roles sent to 1.0 switches with
100 // Nicira role support, are MASTER and SLAVE
101 roleToSend = OFNiciraControllerRole.ROLE_OTHER;
102 log.warn("Sending Nx Role.SLAVE to switch {}.", sw);
103 }
104 int xid = sw.getNextTransactionId();
105 OFExperimenter roleRequest = OFFactories.getFactory(OFVersion.OF_10)
106 .buildNiciraControllerRoleRequest()
107 .setXid(xid)
108 .setRole(roleToSend)
109 .build();
110 sw.write(Collections.<OFMessage>singletonList(roleRequest));
111 return xid;
112 }
113
114 private int sendOF13RoleRequest(RoleState role) throws IOException {
115 // Convert the role enum to the appropriate role to send
116 OFControllerRole roleToSend = OFControllerRole.ROLE_NOCHANGE;
117 switch (role) {
118 case EQUAL:
119 roleToSend = OFControllerRole.ROLE_EQUAL;
120 break;
121 case MASTER:
122 roleToSend = OFControllerRole.ROLE_MASTER;
123 break;
124 case SLAVE:
125 roleToSend = OFControllerRole.ROLE_SLAVE;
126 break;
127 default:
128 log.warn("Sending default role.noChange to switch {}."
129 + " Should only be used for queries.", sw);
130 }
131
132 int xid = sw.getNextTransactionId();
133 OFRoleRequest rrm = OFFactories.getFactory(OFVersion.OF_13)
134 .buildRoleRequest()
135 .setRole(roleToSend)
136 .setXid(xid)
137 //FIXME fix below when we actually use generation ids
138 .setGenerationId(U64.ZERO)
139 .build();
alshabib19fdc122014-10-03 11:38:19 -0700140 sw.write(rrm);
tom7ef8ff92014-09-17 13:08:06 -0700141 return xid;
142 }
143
144 @Override
145 public synchronized boolean sendRoleRequest(RoleState role, RoleRecvStatus exp)
146 throws IOException {
147 this.expectation = exp;
148
149 if (sw.factory().getVersion() == OFVersion.OF_10) {
150 Boolean supportsNxRole = sw.supportNxRole();
151 if (!supportsNxRole) {
152 log.debug("Switch driver indicates no support for Nicira "
153 + "role request messages. Not sending ...");
154 handleUnsentRoleMessage(role,
155 expectation);
156 return false;
157 }
158 // OF1.0 switch with support for NX_ROLE_REQUEST vendor extn.
159 // make Role.EQUAL become Role.SLAVE
alshabib339a3d92014-09-26 17:54:32 -0700160 pendingRole = role;
tom7ef8ff92014-09-17 13:08:06 -0700161 role = (role == RoleState.EQUAL) ? RoleState.SLAVE : role;
162 pendingXid = sendNxRoleRequest(role);
tom7ef8ff92014-09-17 13:08:06 -0700163 requestPending = true;
164 } else {
165 // OF1.3 switch, use OFPT_ROLE_REQUEST message
166 pendingXid = sendOF13RoleRequest(role);
167 pendingRole = role;
168 requestPending = true;
169 }
170 return true;
171 }
172
173 private void handleUnsentRoleMessage(RoleState role,
174 RoleRecvStatus exp) throws IOException {
175 // typically this is triggered for a switch where role messages
176 // are not supported - we confirm that the role being set is
177 // master
178 if (exp != RoleRecvStatus.MATCHED_SET_ROLE) {
179
180 log.error("Expected MASTER role from registry for switch "
181 + "which has no support for role-messages."
182 + "Received {}. It is possible that this switch "
183 + "is connected to other controllers, in which "
184 + "case it should support role messages - not "
185 + "moving forward.", role);
186
187 }
188
189 }
190
191
192 @Override
193 public synchronized RoleRecvStatus deliverRoleReply(RoleReplyInfo rri)
194 throws SwitchStateException {
195 if (!requestPending) {
196 RoleState currentRole = (sw != null) ? sw.getRole() : null;
197 if (currentRole != null) {
198 if (currentRole == rri.getRole()) {
199 // Don't disconnect if the role reply we received is
200 // for the same role we are already in.
201 log.debug("Received unexpected RoleReply from "
202 + "Switch: {}. "
203 + "Role in reply is same as current role of this "
204 + "controller for this sw. Ignoring ...",
205 sw.getStringId());
206 return RoleRecvStatus.OTHER_EXPECTATION;
207 } else {
208 String msg = String.format("Switch: [%s], "
209 + "received unexpected RoleReply[%s]. "
210 + "No roles are pending, and this controller's "
211 + "current role:[%s] does not match reply. "
212 + "Disconnecting switch ... ",
213 sw.getStringId(),
214 rri, currentRole);
215 throw new SwitchStateException(msg);
216 }
217 }
218 log.debug("Received unexpected RoleReply {} from "
219 + "Switch: {}. "
220 + "This controller has no current role for this sw. "
221 + "Ignoring ...", new Object[] {rri,
222 sw.getStringId(), });
223 return RoleRecvStatus.OTHER_EXPECTATION;
224 }
225
226 int xid = (int) rri.getXid();
227 RoleState role = rri.getRole();
228 // XXX S should check generation id meaningfully and other cases of expectations
229
230 if (pendingXid != xid) {
231 log.debug("Received older role reply from " +
232 "switch {} ({}). Ignoring. " +
233 "Waiting for {}, xid={}",
234 new Object[] {sw.getStringId(), rri,
235 pendingRole, pendingXid });
236 return RoleRecvStatus.OLD_REPLY;
237 }
238
239 if (pendingRole == role) {
240 log.debug("Received role reply message from {} that matched "
241 + "expected role-reply {} with expectations {}",
242 new Object[] {sw.getStringId(), role, expectation});
243
244 if (expectation == RoleRecvStatus.MATCHED_CURRENT_ROLE ||
245 expectation == RoleRecvStatus.MATCHED_SET_ROLE) {
246 return expectation;
247 } else {
248 return RoleRecvStatus.OTHER_EXPECTATION;
249 }
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700250 } else {
251 sw.returnRoleAssertFailure(pendingRole);
tom7ef8ff92014-09-17 13:08:06 -0700252 }
253
254 // if xids match but role's don't, perhaps its a query (OF1.3)
255 if (expectation == RoleRecvStatus.REPLY_QUERY) {
256 return expectation;
257 }
258
259 return RoleRecvStatus.OTHER_EXPECTATION;
260 }
261
262 /**
263 * Called if we receive an error message. If the xid matches the
264 * pending request we handle it otherwise we ignore it.
265 *
266 * Note: since we only keep the last pending request we might get
267 * error messages for earlier role requests that we won't be able
268 * to handle
269 */
270 @Override
271 public synchronized RoleRecvStatus deliverError(OFErrorMsg error)
272 throws SwitchStateException {
273 if (!requestPending) {
274 log.debug("Received an error msg from sw {}, but no pending "
275 + "requests in role-changer; not handling ...",
276 sw.getStringId());
277 return RoleRecvStatus.OTHER_EXPECTATION;
278 }
279 if (pendingXid != error.getXid()) {
280 if (error.getErrType() == OFErrorType.ROLE_REQUEST_FAILED) {
281 log.debug("Received an error msg from sw {} for a role request,"
282 + " but not for pending request in role-changer; "
283 + " ignoring error {} ...",
284 sw.getStringId(), error);
285 }
286 return RoleRecvStatus.OTHER_EXPECTATION;
287 }
288 // it is an error related to a currently pending role request message
289 if (error.getErrType() == OFErrorType.BAD_REQUEST) {
290 log.error("Received a error msg {} from sw {} for "
291 + "pending role request {}. Switch driver indicates "
292 + "role-messaging is supported. Possible issues in "
293 + "switch driver configuration?", new Object[] {
294 ((OFBadRequestErrorMsg) error).toString(),
295 sw.getStringId(), pendingRole
296 });
297 return RoleRecvStatus.UNSUPPORTED;
298 }
299
300 if (error.getErrType() == OFErrorType.ROLE_REQUEST_FAILED) {
301 OFRoleRequestFailedErrorMsg rrerr =
302 (OFRoleRequestFailedErrorMsg) error;
303 switch (rrerr.getCode()) {
304 case BAD_ROLE:
305 // switch says that current-role-req has bad role?
306 // for now we disconnect
307 // fall-thru
308 case STALE:
309 // switch says that current-role-req has stale gen-id?
310 // for now we disconnect
311 // fall-thru
312 case UNSUP:
313 // switch says that current-role-req has role that
314 // cannot be supported? for now we disconnect
315 String msgx = String.format("Switch: [%s], "
316 + "received Error to for pending role request [%s]. "
317 + "Error:[%s]. Disconnecting switch ... ",
318 sw.getStringId(),
319 pendingRole, rrerr);
320 throw new SwitchStateException(msgx);
321 default:
322 break;
323 }
324 }
325
326 // This error message was for a role request message but we dont know
327 // how to handle errors for nicira role request messages
328 return RoleRecvStatus.OTHER_EXPECTATION;
329 }
330
331 /**
332 * Extract the role from an OFVendor message.
333 *
334 * Extract the role from an OFVendor message if the message is a
335 * Nicira role reply. Otherwise return null.
336 *
337 * @param experimenterMsg message
338 * @return The role in the message if the message is a Nicira role
339 * reply, null otherwise.
340 * @throws SwitchStateException If the message is a Nicira role reply
341 * but the numeric role value is unknown.
342 */
343 @Override
344 public RoleState extractNiciraRoleReply(OFExperimenter experimenterMsg)
345 throws SwitchStateException {
346 int vendor = (int) experimenterMsg.getExperimenter();
347 if (vendor != 0x2320) {
348 return null;
349 }
350 OFNiciraControllerRoleReply nrr =
351 (OFNiciraControllerRoleReply) experimenterMsg;
352
353 RoleState role = null;
354 OFNiciraControllerRole ncr = nrr.getRole();
355 switch(ncr) {
356 case ROLE_MASTER:
357 role = RoleState.MASTER;
358 break;
359 case ROLE_OTHER:
360 role = RoleState.EQUAL;
361 break;
362 case ROLE_SLAVE:
363 role = RoleState.SLAVE;
364 break;
365 default: //handled below
366 }
367
368 if (role == null) {
369 String msg = String.format("Switch: [%s], "
370 + "received NX_ROLE_REPLY with invalid role "
371 + "value %s",
372 sw.getStringId(),
373 nrr.getRole());
374 throw new SwitchStateException(msg);
375 }
376 return role;
377 }
378
379 /**
380 * Extract the role information from an OF1.3 Role Reply Message.
381 *
382 * @param rrmsg the role message
383 * @return RoleReplyInfo object
384 * @throws SwitchStateException if the role information could not be extracted.
385 */
386 @Override
387 public RoleReplyInfo extractOFRoleReply(OFRoleReply rrmsg)
388 throws SwitchStateException {
389 OFControllerRole cr = rrmsg.getRole();
390 RoleState role = null;
391 switch(cr) {
392 case ROLE_EQUAL:
393 role = RoleState.EQUAL;
394 break;
395 case ROLE_MASTER:
396 role = RoleState.MASTER;
397 break;
398 case ROLE_SLAVE:
399 role = RoleState.SLAVE;
400 break;
401 case ROLE_NOCHANGE: // switch should send current role
402 default:
403 String msg = String.format("Unknown controller role %s "
404 + "received from switch %s", cr, sw);
405 throw new SwitchStateException(msg);
406 }
407
408 return new RoleReplyInfo(role, rrmsg.getGenerationId(), rrmsg.getXid());
409 }
410
411}
412