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