blob: f2e9581a97abd6bc0b3d1d1e14c0e57813ef38da [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 *
72 * @param sw switch to send the role request message to
73 * @param role role to request
74 */
75 private int sendNxRoleRequest(RoleState role) throws IOException {
76 // Convert the role enum to the appropriate role to send
77 OFNiciraControllerRole roleToSend = OFNiciraControllerRole.ROLE_OTHER;
78 switch (role) {
79 case MASTER:
80 roleToSend = OFNiciraControllerRole.ROLE_MASTER;
81 break;
82 case SLAVE:
83 case EQUAL:
84 default:
85 // ensuring that the only two roles sent to 1.0 switches with
86 // Nicira role support, are MASTER and SLAVE
alshabibdf652ad2014-09-09 11:53:19 -070087 roleToSend = OFNiciraControllerRole.ROLE_OTHER;
alshabib54ebd9c2014-08-27 18:38:41 -070088 log.warn("Sending Nx Role.SLAVE to switch {}.", sw);
89 }
alshabibd777b202014-08-28 17:52:55 -070090 int xid = sw.getNextTransactionId();
alshabib54ebd9c2014-08-27 18:38:41 -070091 OFExperimenter roleRequest = OFFactories.getFactory(OFVersion.OF_10)
92 .buildNiciraControllerRoleRequest()
93 .setXid(xid)
94 .setRole(roleToSend)
95 .build();
96 sw.write(Collections.<OFMessage>singletonList(roleRequest));
97 return xid;
98 }
99
100 private int sendOF13RoleRequest(RoleState role) throws IOException {
101 // Convert the role enum to the appropriate role to send
102 OFControllerRole roleToSend = OFControllerRole.ROLE_NOCHANGE;
103 switch (role) {
104 case EQUAL:
105 roleToSend = OFControllerRole.ROLE_EQUAL;
106 break;
107 case MASTER:
108 roleToSend = OFControllerRole.ROLE_MASTER;
109 break;
110 case SLAVE:
111 roleToSend = OFControllerRole.ROLE_SLAVE;
112 break;
113 default:
114 log.warn("Sending default role.noChange to switch {}."
115 + " Should only be used for queries.", sw);
116 }
117
alshabibd777b202014-08-28 17:52:55 -0700118 int xid = sw.getNextTransactionId();
alshabib54ebd9c2014-08-27 18:38:41 -0700119 OFRoleRequest rrm = OFFactories.getFactory(OFVersion.OF_13)
120 .buildRoleRequest()
121 .setRole(roleToSend)
122 .setXid(xid)
123 //FIXME fix below when we actually use generation ids
124 .setGenerationId(U64.ZERO)
125 .build();
alshabibd777b202014-08-28 17:52:55 -0700126 sw.sendMsg(rrm);
alshabib54ebd9c2014-08-27 18:38:41 -0700127 return xid;
128 }
129
alshabib6171f182014-09-02 19:00:32 -0700130 @Override
131 public synchronized boolean sendRoleRequest(RoleState role, RoleRecvStatus exp)
alshabib54ebd9c2014-08-27 18:38:41 -0700132 throws IOException {
133 this.expectation = exp;
134
135 if (sw.factory().getVersion() == OFVersion.OF_10) {
alshabib6171f182014-09-02 19:00:32 -0700136 Boolean supportsNxRole = sw.supportNxRole();
alshabib54ebd9c2014-08-27 18:38:41 -0700137 if (!supportsNxRole) {
138 log.debug("Switch driver indicates no support for Nicira "
139 + "role request messages. Not sending ...");
140 handleUnsentRoleMessage(role,
141 expectation);
142 return false;
143 }
144 // OF1.0 switch with support for NX_ROLE_REQUEST vendor extn.
145 // make Role.EQUAL become Role.SLAVE
146 role = (role == RoleState.EQUAL) ? RoleState.SLAVE : role;
147 pendingXid = sendNxRoleRequest(role);
148 pendingRole = role;
149 requestPending = true;
150 } else {
151 // OF1.3 switch, use OFPT_ROLE_REQUEST message
152 pendingXid = sendOF13RoleRequest(role);
153 pendingRole = role;
154 requestPending = true;
155 }
156 return true;
157 }
158
159 private void handleUnsentRoleMessage(RoleState role,
160 RoleRecvStatus exp) throws IOException {
161 // typically this is triggered for a switch where role messages
162 // are not supported - we confirm that the role being set is
163 // master
164 if (exp != RoleRecvStatus.MATCHED_SET_ROLE) {
165
166 log.error("Expected MASTER role from registry for switch "
167 + "which has no support for role-messages."
168 + "Received {}. It is possible that this switch "
169 + "is connected to other controllers, in which "
170 + "case it should support role messages - not "
171 + "moving forward.", role);
172
173 }
174
175 }
176
alshabib6171f182014-09-02 19:00:32 -0700177
178 @Override
179 public synchronized RoleRecvStatus deliverRoleReply(RoleReplyInfo rri)
alshabib54ebd9c2014-08-27 18:38:41 -0700180 throws SwitchStateException {
181 if (!requestPending) {
182 RoleState currentRole = (sw != null) ? sw.getRole() : null;
183 if (currentRole != null) {
184 if (currentRole == rri.getRole()) {
185 // Don't disconnect if the role reply we received is
186 // for the same role we are already in.
187 log.debug("Received unexpected RoleReply from "
188 + "Switch: {}. "
189 + "Role in reply is same as current role of this "
190 + "controller for this sw. Ignoring ...",
191 sw.getStringId());
192 return RoleRecvStatus.OTHER_EXPECTATION;
193 } else {
194 String msg = String.format("Switch: [%s], "
195 + "received unexpected RoleReply[%s]. "
196 + "No roles are pending, and this controller's "
197 + "current role:[%s] does not match reply. "
198 + "Disconnecting switch ... ",
199 sw.getStringId(),
200 rri, currentRole);
201 throw new SwitchStateException(msg);
202 }
203 }
204 log.debug("Received unexpected RoleReply {} from "
205 + "Switch: {}. "
206 + "This controller has no current role for this sw. "
207 + "Ignoring ...", new Object[] {rri,
208 sw.getStringId(), });
209 return RoleRecvStatus.OTHER_EXPECTATION;
210 }
211
212 int xid = (int) rri.getXid();
213 RoleState role = rri.getRole();
214 // XXX S should check generation id meaningfully and other cases of expectations
alshabib54ebd9c2014-08-27 18:38:41 -0700215
216 if (pendingXid != xid) {
217 log.debug("Received older role reply from " +
218 "switch {} ({}). Ignoring. " +
219 "Waiting for {}, xid={}",
220 new Object[] {sw.getStringId(), rri,
221 pendingRole, pendingXid });
222 return RoleRecvStatus.OLD_REPLY;
223 }
224
225 if (pendingRole == role) {
226 log.debug("Received role reply message from {} that matched "
227 + "expected role-reply {} with expectations {}",
228 new Object[] {sw.getStringId(), role, expectation});
229
alshabib54ebd9c2014-08-27 18:38:41 -0700230 if (expectation == RoleRecvStatus.MATCHED_CURRENT_ROLE ||
231 expectation == RoleRecvStatus.MATCHED_SET_ROLE) {
232 return expectation;
233 } else {
234 return RoleRecvStatus.OTHER_EXPECTATION;
235 }
236 }
237
238 // if xids match but role's don't, perhaps its a query (OF1.3)
239 if (expectation == RoleRecvStatus.REPLY_QUERY) {
240 return expectation;
241 }
242
243 return RoleRecvStatus.OTHER_EXPECTATION;
244 }
245
246 /**
247 * Called if we receive an error message. If the xid matches the
248 * pending request we handle it otherwise we ignore it.
249 *
250 * Note: since we only keep the last pending request we might get
251 * error messages for earlier role requests that we won't be able
252 * to handle
253 */
alshabib6171f182014-09-02 19:00:32 -0700254 @Override
255 public synchronized RoleRecvStatus deliverError(OFErrorMsg error)
alshabib54ebd9c2014-08-27 18:38:41 -0700256 throws SwitchStateException {
257 if (!requestPending) {
258 log.debug("Received an error msg from sw {}, but no pending "
259 + "requests in role-changer; not handling ...",
alshabib6171f182014-09-02 19:00:32 -0700260 sw.getStringId());
alshabib54ebd9c2014-08-27 18:38:41 -0700261 return RoleRecvStatus.OTHER_EXPECTATION;
262 }
263 if (pendingXid != error.getXid()) {
264 if (error.getErrType() == OFErrorType.ROLE_REQUEST_FAILED) {
265 log.debug("Received an error msg from sw {} for a role request,"
266 + " but not for pending request in role-changer; "
267 + " ignoring error {} ...",
268 sw.getStringId(), error);
269 }
270 return RoleRecvStatus.OTHER_EXPECTATION;
271 }
272 // it is an error related to a currently pending role request message
273 if (error.getErrType() == OFErrorType.BAD_REQUEST) {
274 log.error("Received a error msg {} from sw {} for "
275 + "pending role request {}. Switch driver indicates "
276 + "role-messaging is supported. Possible issues in "
277 + "switch driver configuration?", new Object[] {
278 ((OFBadRequestErrorMsg) error).toString(),
279 sw.getStringId(), pendingRole
280 });
281 return RoleRecvStatus.UNSUPPORTED;
282 }
283
284 if (error.getErrType() == OFErrorType.ROLE_REQUEST_FAILED) {
285 OFRoleRequestFailedErrorMsg rrerr =
286 (OFRoleRequestFailedErrorMsg) error;
287 switch (rrerr.getCode()) {
288 case BAD_ROLE:
289 // switch says that current-role-req has bad role?
290 // for now we disconnect
291 // fall-thru
292 case STALE:
293 // switch says that current-role-req has stale gen-id?
294 // for now we disconnect
295 // fall-thru
296 case UNSUP:
297 // switch says that current-role-req has role that
298 // cannot be supported? for now we disconnect
299 String msgx = String.format("Switch: [%s], "
300 + "received Error to for pending role request [%s]. "
301 + "Error:[%s]. Disconnecting switch ... ",
302 sw.getStringId(),
303 pendingRole, rrerr);
304 throw new SwitchStateException(msgx);
305 default:
306 break;
307 }
308 }
309
310 // This error message was for a role request message but we dont know
311 // how to handle errors for nicira role request messages
312 return RoleRecvStatus.OTHER_EXPECTATION;
313 }
314
315 /**
316 * Extract the role from an OFVendor message.
317 *
318 * Extract the role from an OFVendor message if the message is a
319 * Nicira role reply. Otherwise return null.
320 *
321 * @param h The channel handler receiving the message
322 * @param vendorMessage The vendor message to parse.
323 * @return The role in the message if the message is a Nicira role
324 * reply, null otherwise.
325 * @throws SwitchStateException If the message is a Nicira role reply
326 * but the numeric role value is unknown.
327 */
alshabib6171f182014-09-02 19:00:32 -0700328 @Override
329 public RoleState extractNiciraRoleReply(OFExperimenter experimenterMsg)
alshabib54ebd9c2014-08-27 18:38:41 -0700330 throws SwitchStateException {
331 int vendor = (int) experimenterMsg.getExperimenter();
332 if (vendor != 0x2320) {
333 return null;
334 }
335 OFNiciraControllerRoleReply nrr =
336 (OFNiciraControllerRoleReply) experimenterMsg;
337
338 RoleState role = null;
339 OFNiciraControllerRole ncr = nrr.getRole();
340 switch(ncr) {
341 case ROLE_MASTER:
342 role = RoleState.MASTER;
343 break;
344 case ROLE_OTHER:
345 role = RoleState.EQUAL;
346 break;
347 case ROLE_SLAVE:
348 role = RoleState.SLAVE;
349 break;
350 default: //handled below
351 }
352
353 if (role == null) {
354 String msg = String.format("Switch: [%s], "
355 + "received NX_ROLE_REPLY with invalid role "
356 + "value %s",
357 sw.getStringId(),
358 nrr.getRole());
359 throw new SwitchStateException(msg);
360 }
361 return role;
362 }
363
364 /**
alshabib54ebd9c2014-08-27 18:38:41 -0700365 * Extract the role information from an OF1.3 Role Reply Message.
366 * @param h
alshabib6171f182014-09-02 19:00:32 -0700367 * @param rrmsg the role message
alshabib54ebd9c2014-08-27 18:38:41 -0700368 * @return RoleReplyInfo object
alshabib6171f182014-09-02 19:00:32 -0700369 * @throws SwitchStateException if the role information could not be extracted.
alshabib54ebd9c2014-08-27 18:38:41 -0700370 */
alshabib6171f182014-09-02 19:00:32 -0700371 @Override
372 public RoleReplyInfo extractOFRoleReply(OFRoleReply rrmsg)
alshabib54ebd9c2014-08-27 18:38:41 -0700373 throws SwitchStateException {
374 OFControllerRole cr = rrmsg.getRole();
375 RoleState role = null;
376 switch(cr) {
377 case ROLE_EQUAL:
378 role = RoleState.EQUAL;
379 break;
380 case ROLE_MASTER:
381 role = RoleState.MASTER;
382 break;
383 case ROLE_SLAVE:
384 role = RoleState.SLAVE;
385 break;
386 case ROLE_NOCHANGE: // switch should send current role
387 default:
388 String msg = String.format("Unknown controller role %s "
389 + "received from switch %s", cr, sw);
390 throw new SwitchStateException(msg);
391 }
392
393 return new RoleReplyInfo(role, rrmsg.getGenerationId(), rrmsg.getXid());
394 }
395
396}
397