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