blob: 2f173240b67778381e94fe5616c41fa74f441294 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07002 * Copyright 2014 Open Networking Laboratory
tom7ef8ff92014-09-17 13:08:06 -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
tom7ef8ff92014-09-17 13:08:06 -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 */
tom7ef8ff92014-09-17 13:08:06 -070016
Brian O'Connorabafb502014-12-02 22:26:20 -080017package org.onosproject.openflow.controller.driver;
tom7ef8ff92014-09-17 13:08:06 -070018
19import java.io.IOException;
Ray Milkeye53f1712015-01-16 09:17:16 -080020import java.net.InetSocketAddress;
21import java.net.SocketAddress;
tom7ef8ff92014-09-17 13:08:06 -070022import java.util.Collections;
23import java.util.List;
Thomas Vachuskaf15511b2015-03-24 12:17:57 -070024import java.util.concurrent.RejectedExecutionException;
tom7ef8ff92014-09-17 13:08:06 -070025import java.util.concurrent.atomic.AtomicInteger;
26
27import org.jboss.netty.channel.Channel;
Ray Milkeye53f1712015-01-16 09:17:16 -080028import org.onlab.packet.IpAddress;
Brian O'Connorabafb502014-12-02 22:26:20 -080029import org.onosproject.openflow.controller.Dpid;
30import org.onosproject.openflow.controller.RoleState;
tom7ef8ff92014-09-17 13:08:06 -070031import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
32import org.projectfloodlight.openflow.protocol.OFErrorMsg;
33import org.projectfloodlight.openflow.protocol.OFExperimenter;
34import org.projectfloodlight.openflow.protocol.OFFactories;
35import org.projectfloodlight.openflow.protocol.OFFactory;
36import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
37import org.projectfloodlight.openflow.protocol.OFMessage;
38import org.projectfloodlight.openflow.protocol.OFPortDesc;
39import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
Thomas Vachuska39274462014-12-02 13:23:50 -080040import org.projectfloodlight.openflow.protocol.OFPortStatus;
tom7ef8ff92014-09-17 13:08:06 -070041import org.projectfloodlight.openflow.protocol.OFRoleReply;
42import org.projectfloodlight.openflow.protocol.OFVersion;
43import org.slf4j.Logger;
44import org.slf4j.LoggerFactory;
45
46/**
47 * An abstract representation of an OpenFlow switch. Can be extended by others
48 * to serve as a base for their vendor specific representation of a switch.
49 */
50public abstract class AbstractOpenFlowSwitch implements OpenFlowSwitchDriver {
51
Yuta HIGUCHIf5416d82014-10-24 21:17:40 -070052 protected final Logger log = LoggerFactory.getLogger(getClass());
tom7ef8ff92014-09-17 13:08:06 -070053
Thomas Vachuskaf15511b2015-03-24 12:17:57 -070054 private static final String SHUTDOWN_MSG = "Worker has already been shutdown";
55
tom7ef8ff92014-09-17 13:08:06 -070056 protected Channel channel;
Ray Milkeye53f1712015-01-16 09:17:16 -080057 protected String channelId;
tom7ef8ff92014-09-17 13:08:06 -070058
59 private boolean connected;
60 protected boolean startDriverHandshakeCalled = false;
61 private final Dpid dpid;
62 private OpenFlowAgent agent;
63 private final AtomicInteger xidCounter = new AtomicInteger(0);
64
65 private OFVersion ofVersion;
66
67 protected OFPortDescStatsReply ports;
68
69 protected boolean tableFull;
70
71 private RoleHandler roleMan;
72
73 protected RoleState role;
74
75 protected OFFeaturesReply features;
76 protected OFDescStatsReply desc;
77
78 /**
79 * Given a dpid build this switch.
80 * @param dp the dpid
81 */
82 protected AbstractOpenFlowSwitch(Dpid dp) {
83 this.dpid = dp;
84 }
85
86 public AbstractOpenFlowSwitch(Dpid dpid, OFDescStatsReply desc) {
87 this.dpid = dpid;
88 this.desc = desc;
89 }
90
91 //************************
92 // Channel related
93 //************************
94
95 @Override
96 public final void disconnectSwitch() {
97 this.channel.close();
98 }
99
100 @Override
101 public final void sendMsg(OFMessage m) {
alshabib339a3d92014-09-26 17:54:32 -0700102 if (role == RoleState.MASTER) {
Thomas Vachuskaf15511b2015-03-24 12:17:57 -0700103 try {
104 this.write(m);
105 } catch (RejectedExecutionException e) {
106 if (!e.getMessage().contains(SHUTDOWN_MSG)) {
107 throw e;
108 }
109 }
alshabib339a3d92014-09-26 17:54:32 -0700110 }
tom7ef8ff92014-09-17 13:08:06 -0700111 }
112
113 @Override
114 public final void sendMsg(List<OFMessage> msgs) {
alshabib339a3d92014-09-26 17:54:32 -0700115 if (role == RoleState.MASTER) {
Thomas Vachuskaf15511b2015-03-24 12:17:57 -0700116 try {
117 this.write(msgs);
118 } catch (RejectedExecutionException e) {
119 if (!e.getMessage().contains(SHUTDOWN_MSG)) {
120 throw e;
121 }
122 }
alshabib339a3d92014-09-26 17:54:32 -0700123 }
tom7ef8ff92014-09-17 13:08:06 -0700124 }
125
126 @Override
127 public abstract void write(OFMessage msg);
128
129 @Override
130 public abstract void write(List<OFMessage> msgs);
131
132 @Override
133 public final boolean isConnected() {
134 return this.connected;
135 }
136
137 @Override
138 public final void setConnected(boolean connected) {
139 this.connected = connected;
140 };
141
142 @Override
143 public final void setChannel(Channel channel) {
144 this.channel = channel;
Ray Milkeye53f1712015-01-16 09:17:16 -0800145 final SocketAddress address = channel.getRemoteAddress();
146 if (address instanceof InetSocketAddress) {
147 final InetSocketAddress inetAddress = (InetSocketAddress) address;
148 final IpAddress ipAddress = IpAddress.valueOf(inetAddress.getAddress());
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700149 if (ipAddress.isIp4()) {
Ray Milkeye53f1712015-01-16 09:17:16 -0800150 channelId = ipAddress.toString() + ':' + inetAddress.getPort();
151 } else {
152 channelId = '[' + ipAddress.toString() + "]:" + inetAddress.getPort();
153 }
154 }
tom7ef8ff92014-09-17 13:08:06 -0700155 };
156
Ray Milkeye53f1712015-01-16 09:17:16 -0800157 @Override
158 public String channelId() {
159 return channelId;
160 }
161
162
tom7ef8ff92014-09-17 13:08:06 -0700163 //************************
164 // Switch features related
165 //************************
166
167 @Override
168 public final long getId() {
169 return this.dpid.value();
170 };
171
172 @Override
173 public final String getStringId() {
174 return this.dpid.toString();
175 }
176
177 @Override
178 public final void setOFVersion(OFVersion ofV) {
179 this.ofVersion = ofV;
180 }
181
182 @Override
183 public void setTableFull(boolean full) {
184 this.tableFull = full;
185 }
186
187 @Override
188 public void setFeaturesReply(OFFeaturesReply featuresReply) {
189 this.features = featuresReply;
190 }
191
192 @Override
193 public abstract Boolean supportNxRole();
194
195 //************************
196 // Message handling
197 //************************
198 /**
199 * Handle the message coming from the dataplane.
200 *
201 * @param m the actual message
202 */
203 @Override
204 public final void handleMessage(OFMessage m) {
Thomas Vachuska39274462014-12-02 13:23:50 -0800205 if (this.role == RoleState.MASTER || m instanceof OFPortStatus) {
alshabib339a3d92014-09-26 17:54:32 -0700206 this.agent.processMessage(dpid, m);
207 }
tom7ef8ff92014-09-17 13:08:06 -0700208 }
209
210 @Override
211 public RoleState getRole() {
212 return role;
213 };
214
215 @Override
216 public final boolean connectSwitch() {
217 return this.agent.addConnectedSwitch(dpid, this);
218 }
219
220 @Override
221 public final boolean activateMasterSwitch() {
222 return this.agent.addActivatedMasterSwitch(dpid, this);
223 }
224
225 @Override
226 public final boolean activateEqualSwitch() {
227 return this.agent.addActivatedEqualSwitch(dpid, this);
228 }
229
230 @Override
231 public final void transitionToEqualSwitch() {
232 this.agent.transitionToEqualSwitch(dpid);
233 }
234
235 @Override
236 public final void transitionToMasterSwitch() {
237 this.agent.transitionToMasterSwitch(dpid);
238 }
239
240 @Override
241 public final void removeConnectedSwitch() {
242 this.agent.removeConnectedSwitch(dpid);
243 }
244
245 @Override
246 public OFFactory factory() {
247 return OFFactories.getFactory(ofVersion);
248 }
249
250 @Override
251 public void setPortDescReply(OFPortDescStatsReply portDescReply) {
252 this.ports = portDescReply;
253 }
254
255 @Override
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700256 public void returnRoleReply(RoleState requested, RoleState response) {
257 this.agent.returnRoleReply(dpid, requested, response);
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700258 }
259
260 @Override
tom7ef8ff92014-09-17 13:08:06 -0700261 public abstract void startDriverHandshake();
262
263 @Override
264 public abstract boolean isDriverHandshakeComplete();
265
266 @Override
267 public abstract void processDriverHandshakeMessage(OFMessage m);
268
alshabib339a3d92014-09-26 17:54:32 -0700269
270 // Role Handling
271
tom7ef8ff92014-09-17 13:08:06 -0700272 @Override
273 public void setRole(RoleState role) {
274 try {
tom7ef8ff92014-09-17 13:08:06 -0700275 if (this.roleMan.sendRoleRequest(role, RoleRecvStatus.MATCHED_SET_ROLE)) {
alshabib339a3d92014-09-26 17:54:32 -0700276 log.info("Sending role {} to switch {}", role, getStringId());
277 if (role == RoleState.SLAVE || role == RoleState.EQUAL) {
278 this.role = role;
279 }
alshabib7814e9f2014-09-30 11:52:12 -0700280 } else {
281 this.role = role;
tom7ef8ff92014-09-17 13:08:06 -0700282 }
283 } catch (IOException e) {
284 log.error("Unable to write to switch {}.", this.dpid);
285 }
286 }
287
alshabib339a3d92014-09-26 17:54:32 -0700288 @Override
289 public void reassertRole() {
290 if (this.getRole() == RoleState.MASTER) {
291 log.warn("Received permission error from switch {} while " +
292 "being master. Reasserting master role.",
293 this.getStringId());
294 this.setRole(RoleState.MASTER);
295 }
296 }
297
298
tom7ef8ff92014-09-17 13:08:06 -0700299
300 @Override
301 public void handleRole(OFMessage m) throws SwitchStateException {
302 RoleReplyInfo rri = roleMan.extractOFRoleReply((OFRoleReply) m);
303 RoleRecvStatus rrs = roleMan.deliverRoleReply(rri);
304 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
305 if (rri.getRole() == RoleState.MASTER) {
alshabib339a3d92014-09-26 17:54:32 -0700306 this.role = rri.getRole();
tom7ef8ff92014-09-17 13:08:06 -0700307 this.transitionToMasterSwitch();
308 } else if (rri.getRole() == RoleState.EQUAL ||
alshabib339a3d92014-09-26 17:54:32 -0700309 rri.getRole() == RoleState.SLAVE) {
tom7ef8ff92014-09-17 13:08:06 -0700310 this.transitionToEqualSwitch();
311 }
alshabib339a3d92014-09-26 17:54:32 -0700312 } else {
alshabib4785eec2014-12-04 16:45:45 -0800313 log.warn("Failed to set role for {}", this.getStringId());
tom7ef8ff92014-09-17 13:08:06 -0700314 }
315 }
316
317 @Override
318 public void handleNiciraRole(OFMessage m) throws SwitchStateException {
319 RoleState r = this.roleMan.extractNiciraRoleReply((OFExperimenter) m);
320 if (r == null) {
321 // The message wasn't really a Nicira role reply. We just
322 // dispatch it to the OFMessage listeners in this case.
323 this.handleMessage(m);
alshabibdfc7afb2014-10-21 20:13:27 -0700324 return;
tom7ef8ff92014-09-17 13:08:06 -0700325 }
326
327 RoleRecvStatus rrs = this.roleMan.deliverRoleReply(
328 new RoleReplyInfo(r, null, m.getXid()));
329 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
330 if (r == RoleState.MASTER) {
alshabib339a3d92014-09-26 17:54:32 -0700331 this.role = r;
tom7ef8ff92014-09-17 13:08:06 -0700332 this.transitionToMasterSwitch();
333 } else if (r == RoleState.EQUAL ||
334 r == RoleState.SLAVE) {
335 this.transitionToEqualSwitch();
336 }
alshabib339a3d92014-09-26 17:54:32 -0700337 } else {
alshabibdfc7afb2014-10-21 20:13:27 -0700338 this.disconnectSwitch();
tom7ef8ff92014-09-17 13:08:06 -0700339 }
340 }
341
342 @Override
343 public boolean handleRoleError(OFErrorMsg error) {
344 try {
345 return RoleRecvStatus.OTHER_EXPECTATION != this.roleMan.deliverError(error);
346 } catch (SwitchStateException e) {
347 this.disconnectSwitch();
348 }
349 return true;
350 }
351
alshabib339a3d92014-09-26 17:54:32 -0700352
tom7ef8ff92014-09-17 13:08:06 -0700353
354 @Override
355 public final void setAgent(OpenFlowAgent ag) {
356 if (this.agent == null) {
357 this.agent = ag;
358 }
359 }
360
361 @Override
362 public final void setRoleHandler(RoleHandler roleHandler) {
363 if (this.roleMan == null) {
364 this.roleMan = roleHandler;
365 }
366 }
367
368 @Override
369 public void setSwitchDescription(OFDescStatsReply d) {
370 this.desc = d;
371 }
372
373 @Override
374 public int getNextTransactionId() {
375 return this.xidCounter.getAndIncrement();
376 }
377
378 @Override
379 public List<OFPortDesc> getPorts() {
380 return Collections.unmodifiableList(ports.getEntries());
381 }
382
383 @Override
Ray Milkeyd3edd032015-01-16 11:38:58 -0800384 public String manufacturerDescription() {
tom7ef8ff92014-09-17 13:08:06 -0700385 return this.desc.getMfrDesc();
386 }
387
388
389 @Override
390 public String datapathDescription() {
391 return this.desc.getDpDesc();
392 }
393
394
395 @Override
396 public String hardwareDescription() {
397 return this.desc.getHwDesc();
398 }
399
400 @Override
401 public String softwareDescription() {
402 return this.desc.getSwDesc();
403 }
404
405 @Override
406 public String serialNumber() {
407 return this.desc.getSerialNum();
408 }
409
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700410 @Override
411 public boolean isOptical() {
412 return false;
413 }
414
alshabib9af70072015-02-09 14:34:16 -0800415
416
tom7ef8ff92014-09-17 13:08:06 -0700417}