blob: 4a1e684d481899e6d65e076ee0cf3db8c0d3dbe0 [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 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.openflow.controller.impl;
tom7ef8ff92014-09-17 13:08:06 -070017
alshabib8f1cf4a2014-09-17 14:44:48 -070018import static org.onlab.util.Tools.namedThreads;
19
alshabib64def642014-12-02 23:27:37 -080020import java.util.Collection;
tom7ef8ff92014-09-17 13:08:06 -070021import java.util.HashSet;
22import java.util.Set;
23import java.util.concurrent.ConcurrentHashMap;
alshabib8f1cf4a2014-09-17 14:44:48 -070024import java.util.concurrent.ExecutorService;
25import java.util.concurrent.Executors;
tom7ef8ff92014-09-17 13:08:06 -070026import java.util.concurrent.locks.Lock;
27import java.util.concurrent.locks.ReentrantLock;
28
alshabib64def642014-12-02 23:27:37 -080029import com.google.common.collect.Lists;
tom7ef8ff92014-09-17 13:08:06 -070030import org.apache.felix.scr.annotations.Activate;
31import org.apache.felix.scr.annotations.Component;
32import org.apache.felix.scr.annotations.Deactivate;
33import org.apache.felix.scr.annotations.Service;
Brian O'Connorabafb502014-12-02 22:26:20 -080034import org.onosproject.openflow.controller.DefaultOpenFlowPacketContext;
35import org.onosproject.openflow.controller.Dpid;
36import org.onosproject.openflow.controller.OpenFlowController;
37import org.onosproject.openflow.controller.OpenFlowEventListener;
38import org.onosproject.openflow.controller.OpenFlowPacketContext;
39import org.onosproject.openflow.controller.OpenFlowSwitch;
40import org.onosproject.openflow.controller.OpenFlowSwitchListener;
41import org.onosproject.openflow.controller.PacketListener;
42import org.onosproject.openflow.controller.RoleState;
43import org.onosproject.openflow.controller.driver.OpenFlowAgent;
Marc De Leenheer631ffce2014-10-28 16:29:07 -070044import org.projectfloodlight.openflow.protocol.OFCircuitPortStatus;
45import org.projectfloodlight.openflow.protocol.OFExperimenter;
alshabib64def642014-12-02 23:27:37 -080046import org.projectfloodlight.openflow.protocol.OFFactories;
47import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry;
48import org.projectfloodlight.openflow.protocol.OFFlowStatsReply;
sangho6a0bb172015-02-05 12:24:48 -080049import org.projectfloodlight.openflow.protocol.OFGroupDescStatsEntry;
50import org.projectfloodlight.openflow.protocol.OFGroupDescStatsReply;
51import org.projectfloodlight.openflow.protocol.OFGroupStatsEntry;
52import org.projectfloodlight.openflow.protocol.OFGroupStatsReply;
tom7ef8ff92014-09-17 13:08:06 -070053import org.projectfloodlight.openflow.protocol.OFMessage;
54import org.projectfloodlight.openflow.protocol.OFPacketIn;
Marc De Leenheer631ffce2014-10-28 16:29:07 -070055import org.projectfloodlight.openflow.protocol.OFPortDesc;
tom7ef8ff92014-09-17 13:08:06 -070056import org.projectfloodlight.openflow.protocol.OFPortStatus;
Ayaka Koshibe38594c22014-10-22 13:36:12 -070057import org.projectfloodlight.openflow.protocol.OFStatsReply;
alshabib64def642014-12-02 23:27:37 -080058import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
tom7ef8ff92014-09-17 13:08:06 -070059import org.slf4j.Logger;
60import org.slf4j.LoggerFactory;
61
62import com.google.common.collect.ArrayListMultimap;
63import com.google.common.collect.Multimap;
alshabibeec3a062014-09-17 18:01:26 -070064import com.google.common.collect.Sets;
tom7ef8ff92014-09-17 13:08:06 -070065
66@Component(immediate = true)
67@Service
68public class OpenFlowControllerImpl implements OpenFlowController {
69
70 private static final Logger log =
71 LoggerFactory.getLogger(OpenFlowControllerImpl.class);
72
Pavlin Radoslavov369c6432014-12-03 16:25:14 -080073 private final ExecutorService executorMsgs =
74 Executors.newFixedThreadPool(32,
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080075 namedThreads("onos-of-event-stats-%d"));
Pavlin Radoslavov369c6432014-12-03 16:25:14 -080076
77 private final ExecutorService executorBarrier =
78 Executors.newFixedThreadPool(4,
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080079 namedThreads("onos-of-event-barrier-%d"));
alshabib8f1cf4a2014-09-17 14:44:48 -070080
tom7ef8ff92014-09-17 13:08:06 -070081 protected ConcurrentHashMap<Dpid, OpenFlowSwitch> connectedSwitches =
82 new ConcurrentHashMap<Dpid, OpenFlowSwitch>();
83 protected ConcurrentHashMap<Dpid, OpenFlowSwitch> activeMasterSwitches =
84 new ConcurrentHashMap<Dpid, OpenFlowSwitch>();
85 protected ConcurrentHashMap<Dpid, OpenFlowSwitch> activeEqualSwitches =
86 new ConcurrentHashMap<Dpid, OpenFlowSwitch>();
87
88 protected OpenFlowSwitchAgent agent = new OpenFlowSwitchAgent();
alshabib8f1cf4a2014-09-17 14:44:48 -070089 protected Set<OpenFlowSwitchListener> ofSwitchListener = new HashSet<>();
tom7ef8ff92014-09-17 13:08:06 -070090
91 protected Multimap<Integer, PacketListener> ofPacketListener =
92 ArrayListMultimap.create();
93
alshabibeec3a062014-09-17 18:01:26 -070094 protected Set<OpenFlowEventListener> ofEventListener = Sets.newHashSet();
tom7ef8ff92014-09-17 13:08:06 -070095
sangho6a0bb172015-02-05 12:24:48 -080096 protected Multimap<Dpid, OFFlowStatsEntry> fullFlowStats =
97 ArrayListMultimap.create();
98
99 protected Multimap<Dpid, OFGroupStatsEntry> fullGroupStats =
100 ArrayListMultimap.create();
101
102 protected Multimap<Dpid, OFGroupDescStatsEntry> fullGroupDescStats =
alshabib64def642014-12-02 23:27:37 -0800103 ArrayListMultimap.create();
104
tom7ef8ff92014-09-17 13:08:06 -0700105 private final Controller ctrl = new Controller();
106
107 @Activate
108 public void activate() {
109 ctrl.start(agent);
110 }
111
112 @Deactivate
113 public void deactivate() {
114 ctrl.stop();
115 }
116
117 @Override
118 public Iterable<OpenFlowSwitch> getSwitches() {
119 return connectedSwitches.values();
120 }
121
122 @Override
123 public Iterable<OpenFlowSwitch> getMasterSwitches() {
124 return activeMasterSwitches.values();
125 }
126
127 @Override
128 public Iterable<OpenFlowSwitch> getEqualSwitches() {
129 return activeEqualSwitches.values();
130 }
131
132 @Override
133 public OpenFlowSwitch getSwitch(Dpid dpid) {
134 return connectedSwitches.get(dpid);
135 }
136
137 @Override
138 public OpenFlowSwitch getMasterSwitch(Dpid dpid) {
139 return activeMasterSwitches.get(dpid);
140 }
141
142 @Override
143 public OpenFlowSwitch getEqualSwitch(Dpid dpid) {
144 return activeEqualSwitches.get(dpid);
145 }
146
147 @Override
148 public void addListener(OpenFlowSwitchListener listener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700149 if (!ofSwitchListener.contains(listener)) {
150 this.ofSwitchListener.add(listener);
tom7ef8ff92014-09-17 13:08:06 -0700151 }
152 }
153
154 @Override
155 public void removeListener(OpenFlowSwitchListener listener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700156 this.ofSwitchListener.remove(listener);
tom7ef8ff92014-09-17 13:08:06 -0700157 }
158
159 @Override
160 public void addPacketListener(int priority, PacketListener listener) {
161 ofPacketListener.put(priority, listener);
162 }
163
164 @Override
165 public void removePacketListener(PacketListener listener) {
166 ofPacketListener.values().remove(listener);
167 }
168
169 @Override
alshabibeec3a062014-09-17 18:01:26 -0700170 public void addEventListener(OpenFlowEventListener listener) {
171 ofEventListener.add(listener);
172 }
173
174 @Override
175 public void removeEventListener(OpenFlowEventListener listener) {
176 ofEventListener.remove(listener);
177 }
178
179 @Override
tom7ef8ff92014-09-17 13:08:06 -0700180 public void write(Dpid dpid, OFMessage msg) {
181 this.getSwitch(dpid).sendMsg(msg);
182 }
183
184 @Override
185 public void processPacket(Dpid dpid, OFMessage msg) {
sangho6a0bb172015-02-05 12:24:48 -0800186 Collection<OFFlowStatsEntry> flowStats;
187 Collection<OFGroupStatsEntry> groupStats;
188 Collection<OFGroupDescStatsEntry> groupDescStats;
189
tom7ef8ff92014-09-17 13:08:06 -0700190 switch (msg.getType()) {
191 case PORT_STATUS:
alshabib8f1cf4a2014-09-17 14:44:48 -0700192 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700193 l.portChanged(dpid, (OFPortStatus) msg);
194 }
195 break;
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700196 case FEATURES_REPLY:
197 for (OpenFlowSwitchListener l : ofSwitchListener) {
198 l.switchChanged(dpid);
199 }
200 break;
tom7ef8ff92014-09-17 13:08:06 -0700201 case PACKET_IN:
202 OpenFlowPacketContext pktCtx = DefaultOpenFlowPacketContext
203 .packetContextFromPacketIn(this.getSwitch(dpid),
204 (OFPacketIn) msg);
205 for (PacketListener p : ofPacketListener.values()) {
206 p.handlePacket(pktCtx);
207 }
208 break;
Pavlin Radoslavov369c6432014-12-03 16:25:14 -0800209 // TODO: Consider using separate threadpool for sensitive messages.
210 // ie. Back to back error could cause us to starve.
211 case FLOW_REMOVED:
212 case ERROR:
213 executorMsgs.submit(new OFMessageHandler(dpid, msg));
214 break;
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700215 case STATS_REPLY:
216 OFStatsReply reply = (OFStatsReply) msg;
sangho6a0bb172015-02-05 12:24:48 -0800217 switch (reply.getStatsType()) {
218 case PORT_DESC:
219 for (OpenFlowSwitchListener l : ofSwitchListener) {
220 l.switchChanged(dpid);
221 }
222 break;
223 case FLOW:
224 flowStats = publishFlowStats(dpid, (OFFlowStatsReply) reply);
225 if (flowStats != null) {
226 OFFlowStatsReply.Builder rep =
227 OFFactories.getFactory(msg.getVersion()).buildFlowStatsReply();
228 rep.setEntries(Lists.newLinkedList(flowStats));
229 executorMsgs.submit(new OFMessageHandler(dpid, rep.build()));
230 }
231 break;
232 case GROUP:
233 groupStats = publishGroupStats(dpid, (OFGroupStatsReply) reply);
234 if (groupStats != null) {
235 OFGroupStatsReply.Builder rep =
236 OFFactories.getFactory(msg.getVersion()).buildGroupStatsReply();
237 rep.setEntries(Lists.newLinkedList(groupStats));
238 rep.setXid(reply.getXid());
239 executorMsgs.submit(new OFMessageHandler(dpid, rep.build()));
240 }
241 break;
242 case GROUP_DESC:
243 groupDescStats = publishGroupDescStats(dpid,
244 (OFGroupDescStatsReply) reply);
245 if (groupDescStats != null) {
246 OFGroupDescStatsReply.Builder rep =
247 OFFactories.getFactory(msg.getVersion()).buildGroupDescStatsReply();
248 rep.setEntries(Lists.newLinkedList(groupDescStats));
249 rep.setXid(reply.getXid());
250 executorMsgs.submit(new OFMessageHandler(dpid, rep.build()));
251 }
252 break;
253 default:
254 log.warn("Unsupported stats type : {}", reply.getStatsType());
alshabib64def642014-12-02 23:27:37 -0800255 }
256 break;
alshabib8f1cf4a2014-09-17 14:44:48 -0700257 case BARRIER_REPLY:
Pavlin Radoslavov369c6432014-12-03 16:25:14 -0800258 executorBarrier.submit(new OFMessageHandler(dpid, msg));
alshabib8f1cf4a2014-09-17 14:44:48 -0700259 break;
Marc De Leenheer631ffce2014-10-28 16:29:07 -0700260 case EXPERIMENTER:
261 // Handle optical port stats
262 if (((OFExperimenter) msg).getExperimenter() == 0x748771) {
263 OFCircuitPortStatus circuitPortStatus = (OFCircuitPortStatus) msg;
264 OFPortStatus.Builder portStatus = this.getSwitch(dpid).factory().buildPortStatus();
265 OFPortDesc.Builder portDesc = this.getSwitch(dpid).factory().buildPortDesc();
266 portDesc.setPortNo(circuitPortStatus.getPortNo())
267 .setHwAddr(circuitPortStatus.getHwAddr())
268 .setName(circuitPortStatus.getName())
269 .setConfig(circuitPortStatus.getConfig())
270 .setState(circuitPortStatus.getState());
271 portStatus.setReason(circuitPortStatus.getReason()).setDesc(portDesc.build());
272 for (OpenFlowSwitchListener l : ofSwitchListener) {
273 l.portChanged(dpid, portStatus.build());
274 }
275 } else {
276 log.warn("Handling experimenter type {} not yet implemented",
277 ((OFExperimenter) msg).getExperimenter(), msg);
278 }
279 break;
tom7ef8ff92014-09-17 13:08:06 -0700280 default:
281 log.warn("Handling message type {} not yet implemented {}",
282 msg.getType(), msg);
283 }
284 }
285
sangho6a0bb172015-02-05 12:24:48 -0800286 private synchronized Collection<OFFlowStatsEntry> publishFlowStats(Dpid dpid,
287 OFFlowStatsReply reply) {
alshabib64def642014-12-02 23:27:37 -0800288 //TODO: Get rid of synchronized
sangho6a0bb172015-02-05 12:24:48 -0800289 fullFlowStats.putAll(dpid, reply.getEntries());
alshabib64def642014-12-02 23:27:37 -0800290 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
sangho6a0bb172015-02-05 12:24:48 -0800291 return fullFlowStats.removeAll(dpid);
292 }
293 return null;
294 }
295
296 private synchronized Collection<OFGroupStatsEntry> publishGroupStats(Dpid dpid,
297 OFGroupStatsReply reply) {
298 //TODO: Get rid of synchronized
299 fullGroupStats.putAll(dpid, reply.getEntries());
300 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
301 return fullGroupStats.removeAll(dpid);
302 }
303 return null;
304 }
305
306 private synchronized Collection<OFGroupDescStatsEntry> publishGroupDescStats(Dpid dpid,
307 OFGroupDescStatsReply reply) {
308 //TODO: Get rid of synchronized
309 fullGroupDescStats.putAll(dpid, reply.getEntries());
310 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
311 return fullGroupDescStats.removeAll(dpid);
alshabib64def642014-12-02 23:27:37 -0800312 }
313 return null;
314 }
315
tom7ef8ff92014-09-17 13:08:06 -0700316 @Override
317 public void setRole(Dpid dpid, RoleState role) {
Yuta HIGUCHI79cbd1c2014-10-02 16:57:57 -0700318 final OpenFlowSwitch sw = getSwitch(dpid);
319 if (sw == null) {
320 log.debug("Switch not connected. Ignoring setRole({}, {})", dpid, role);
321 return;
322 }
323 sw.setRole(role);
tom7ef8ff92014-09-17 13:08:06 -0700324 }
325
326 /**
327 * Implementation of an OpenFlow Agent which is responsible for
328 * keeping track of connected switches and the state in which
329 * they are.
330 */
331 public class OpenFlowSwitchAgent implements OpenFlowAgent {
332
333 private final Logger log = LoggerFactory.getLogger(OpenFlowSwitchAgent.class);
334 private final Lock switchLock = new ReentrantLock();
335
336 @Override
337 public boolean addConnectedSwitch(Dpid dpid, OpenFlowSwitch sw) {
alshabib9eab22f2014-10-20 17:17:31 -0700338
tom7ef8ff92014-09-17 13:08:06 -0700339 if (connectedSwitches.get(dpid) != null) {
340 log.error("Trying to add connectedSwitch but found a previous "
341 + "value for dpid: {}", dpid);
342 return false;
343 } else {
Yuta HIGUCHIeb3f30b2014-10-22 11:34:49 -0700344 log.info("Added switch {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700345 connectedSwitches.put(dpid, sw);
alshabib8f1cf4a2014-09-17 14:44:48 -0700346 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700347 l.switchAdded(dpid);
348 }
349 return true;
350 }
351 }
352
353 @Override
354 public boolean validActivation(Dpid dpid) {
355 if (connectedSwitches.get(dpid) == null) {
356 log.error("Trying to activate switch but is not in "
357 + "connected switches: dpid {}. Aborting ..",
358 dpid);
359 return false;
360 }
361 if (activeMasterSwitches.get(dpid) != null ||
362 activeEqualSwitches.get(dpid) != null) {
363 log.error("Trying to activate switch but it is already "
364 + "activated: dpid {}. Found in activeMaster: {} "
365 + "Found in activeEqual: {}. Aborting ..", new Object[]{
366 dpid,
367 (activeMasterSwitches.get(dpid) == null) ? 'N' : 'Y',
368 (activeEqualSwitches.get(dpid) == null) ? 'N' : 'Y'});
369 return false;
370 }
371 return true;
372 }
373
374
375 @Override
376 public boolean addActivatedMasterSwitch(Dpid dpid, OpenFlowSwitch sw) {
377 switchLock.lock();
378 try {
379 if (!validActivation(dpid)) {
380 return false;
381 }
382 activeMasterSwitches.put(dpid, sw);
383 return true;
384 } finally {
385 switchLock.unlock();
386 }
387 }
388
389 @Override
390 public boolean addActivatedEqualSwitch(Dpid dpid, OpenFlowSwitch sw) {
391 switchLock.lock();
392 try {
393 if (!validActivation(dpid)) {
394 return false;
395 }
396 activeEqualSwitches.put(dpid, sw);
397 log.info("Added Activated EQUAL Switch {}", dpid);
398 return true;
399 } finally {
400 switchLock.unlock();
401 }
402 }
403
404 @Override
405 public void transitionToMasterSwitch(Dpid dpid) {
406 switchLock.lock();
407 try {
408 if (activeMasterSwitches.containsKey(dpid)) {
409 return;
410 }
411 OpenFlowSwitch sw = activeEqualSwitches.remove(dpid);
412 if (sw == null) {
413 sw = getSwitch(dpid);
414 if (sw == null) {
415 log.error("Transition to master called on sw {}, but switch "
416 + "was not found in controller-cache", dpid);
417 return;
418 }
419 }
420 log.info("Transitioned switch {} to MASTER", dpid);
421 activeMasterSwitches.put(dpid, sw);
422 } finally {
423 switchLock.unlock();
424 }
425 }
426
427
428 @Override
429 public void transitionToEqualSwitch(Dpid dpid) {
430 switchLock.lock();
431 try {
432 if (activeEqualSwitches.containsKey(dpid)) {
433 return;
434 }
435 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
436 if (sw == null) {
437 sw = getSwitch(dpid);
438 if (sw == null) {
439 log.error("Transition to equal called on sw {}, but switch "
440 + "was not found in controller-cache", dpid);
441 return;
442 }
443 }
444 log.info("Transitioned switch {} to EQUAL", dpid);
445 activeEqualSwitches.put(dpid, sw);
446 } finally {
447 switchLock.unlock();
448 }
449
450 }
451
452 @Override
453 public void removeConnectedSwitch(Dpid dpid) {
454 connectedSwitches.remove(dpid);
455 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
456 if (sw == null) {
Ayaka Koshibec4047702014-10-07 14:43:52 -0700457 log.warn("sw was null for {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700458 sw = activeEqualSwitches.remove(dpid);
459 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700460 for (OpenFlowSwitchListener l : ofSwitchListener) {
Ayaka Koshibec4047702014-10-07 14:43:52 -0700461 log.warn("removal for {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700462 l.switchRemoved(dpid);
463 }
464 }
465
466 @Override
467 public void processMessage(Dpid dpid, OFMessage m) {
468 processPacket(dpid, m);
469 }
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700470
471 @Override
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700472 public void returnRoleReply(Dpid dpid, RoleState requested, RoleState response) {
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700473 for (OpenFlowSwitchListener l : ofSwitchListener) {
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700474 l.receivedRoleReply(dpid, requested, response);
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700475 }
476 }
tom7ef8ff92014-09-17 13:08:06 -0700477 }
478
alshabib8f1cf4a2014-09-17 14:44:48 -0700479 private final class OFMessageHandler implements Runnable {
480
481 private final OFMessage msg;
482 private final Dpid dpid;
483
484 public OFMessageHandler(Dpid dpid, OFMessage msg) {
485 this.msg = msg;
486 this.dpid = dpid;
487 }
488
489 @Override
490 public void run() {
alshabibeec3a062014-09-17 18:01:26 -0700491 for (OpenFlowEventListener listener : ofEventListener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700492 listener.handleMessage(dpid, msg);
493 }
494 }
495
496 }
497
tom7ef8ff92014-09-17 13:08:06 -0700498}