blob: a98f00000581fb4f2215f8e1512aae73e64b4cf1 [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
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080018import com.google.common.collect.ArrayListMultimap;
alshabib64def642014-12-02 23:27:37 -080019import com.google.common.collect.Lists;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080020import com.google.common.collect.Multimap;
21import com.google.common.collect.Sets;
tom7ef8ff92014-09-17 13:08:06 -070022import org.apache.felix.scr.annotations.Activate;
23import org.apache.felix.scr.annotations.Component;
24import org.apache.felix.scr.annotations.Deactivate;
25import org.apache.felix.scr.annotations.Service;
Brian O'Connorabafb502014-12-02 22:26:20 -080026import org.onosproject.openflow.controller.DefaultOpenFlowPacketContext;
27import org.onosproject.openflow.controller.Dpid;
28import org.onosproject.openflow.controller.OpenFlowController;
29import org.onosproject.openflow.controller.OpenFlowEventListener;
30import org.onosproject.openflow.controller.OpenFlowPacketContext;
31import org.onosproject.openflow.controller.OpenFlowSwitch;
32import org.onosproject.openflow.controller.OpenFlowSwitchListener;
33import org.onosproject.openflow.controller.PacketListener;
34import org.onosproject.openflow.controller.RoleState;
35import org.onosproject.openflow.controller.driver.OpenFlowAgent;
Marc De Leenheer631ffce2014-10-28 16:29:07 -070036import org.projectfloodlight.openflow.protocol.OFCircuitPortStatus;
37import org.projectfloodlight.openflow.protocol.OFExperimenter;
alshabib64def642014-12-02 23:27:37 -080038import org.projectfloodlight.openflow.protocol.OFFactories;
39import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry;
40import org.projectfloodlight.openflow.protocol.OFFlowStatsReply;
sangho6a0bb172015-02-05 12:24:48 -080041import org.projectfloodlight.openflow.protocol.OFGroupDescStatsEntry;
42import org.projectfloodlight.openflow.protocol.OFGroupDescStatsReply;
43import org.projectfloodlight.openflow.protocol.OFGroupStatsEntry;
44import org.projectfloodlight.openflow.protocol.OFGroupStatsReply;
tom7ef8ff92014-09-17 13:08:06 -070045import org.projectfloodlight.openflow.protocol.OFMessage;
46import org.projectfloodlight.openflow.protocol.OFPacketIn;
Marc De Leenheer631ffce2014-10-28 16:29:07 -070047import org.projectfloodlight.openflow.protocol.OFPortDesc;
tom7ef8ff92014-09-17 13:08:06 -070048import org.projectfloodlight.openflow.protocol.OFPortStatus;
Ayaka Koshibe38594c22014-10-22 13:36:12 -070049import org.projectfloodlight.openflow.protocol.OFStatsReply;
alshabib64def642014-12-02 23:27:37 -080050import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
tom7ef8ff92014-09-17 13:08:06 -070051import org.slf4j.Logger;
52import org.slf4j.LoggerFactory;
53
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080054import java.util.Collection;
55import java.util.HashSet;
56import java.util.Set;
57import java.util.concurrent.ConcurrentHashMap;
58import java.util.concurrent.ExecutorService;
59import java.util.concurrent.Executors;
60import java.util.concurrent.locks.Lock;
61import java.util.concurrent.locks.ReentrantLock;
62
63import static org.onlab.util.Tools.groupedThreads;
tom7ef8ff92014-09-17 13:08:06 -070064
65@Component(immediate = true)
66@Service
67public class OpenFlowControllerImpl implements OpenFlowController {
68
69 private static final Logger log =
70 LoggerFactory.getLogger(OpenFlowControllerImpl.class);
71
Pavlin Radoslavov369c6432014-12-03 16:25:14 -080072 private final ExecutorService executorMsgs =
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080073 Executors.newFixedThreadPool(32, groupedThreads("onos/of", "event-stats-%d"));
Pavlin Radoslavov369c6432014-12-03 16:25:14 -080074
75 private final ExecutorService executorBarrier =
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080076 Executors.newFixedThreadPool(4, groupedThreads("onos/of", "event-barrier-%d"));
alshabib8f1cf4a2014-09-17 14:44:48 -070077
tom7ef8ff92014-09-17 13:08:06 -070078 protected ConcurrentHashMap<Dpid, OpenFlowSwitch> connectedSwitches =
79 new ConcurrentHashMap<Dpid, OpenFlowSwitch>();
80 protected ConcurrentHashMap<Dpid, OpenFlowSwitch> activeMasterSwitches =
81 new ConcurrentHashMap<Dpid, OpenFlowSwitch>();
82 protected ConcurrentHashMap<Dpid, OpenFlowSwitch> activeEqualSwitches =
83 new ConcurrentHashMap<Dpid, OpenFlowSwitch>();
84
85 protected OpenFlowSwitchAgent agent = new OpenFlowSwitchAgent();
alshabib8f1cf4a2014-09-17 14:44:48 -070086 protected Set<OpenFlowSwitchListener> ofSwitchListener = new HashSet<>();
tom7ef8ff92014-09-17 13:08:06 -070087
88 protected Multimap<Integer, PacketListener> ofPacketListener =
89 ArrayListMultimap.create();
90
alshabibeec3a062014-09-17 18:01:26 -070091 protected Set<OpenFlowEventListener> ofEventListener = Sets.newHashSet();
tom7ef8ff92014-09-17 13:08:06 -070092
sangho6a0bb172015-02-05 12:24:48 -080093 protected Multimap<Dpid, OFFlowStatsEntry> fullFlowStats =
94 ArrayListMultimap.create();
95
96 protected Multimap<Dpid, OFGroupStatsEntry> fullGroupStats =
97 ArrayListMultimap.create();
98
99 protected Multimap<Dpid, OFGroupDescStatsEntry> fullGroupDescStats =
alshabib64def642014-12-02 23:27:37 -0800100 ArrayListMultimap.create();
101
tom7ef8ff92014-09-17 13:08:06 -0700102 private final Controller ctrl = new Controller();
103
104 @Activate
105 public void activate() {
106 ctrl.start(agent);
107 }
108
109 @Deactivate
110 public void deactivate() {
111 ctrl.stop();
112 }
113
114 @Override
115 public Iterable<OpenFlowSwitch> getSwitches() {
116 return connectedSwitches.values();
117 }
118
119 @Override
120 public Iterable<OpenFlowSwitch> getMasterSwitches() {
121 return activeMasterSwitches.values();
122 }
123
124 @Override
125 public Iterable<OpenFlowSwitch> getEqualSwitches() {
126 return activeEqualSwitches.values();
127 }
128
129 @Override
130 public OpenFlowSwitch getSwitch(Dpid dpid) {
131 return connectedSwitches.get(dpid);
132 }
133
134 @Override
135 public OpenFlowSwitch getMasterSwitch(Dpid dpid) {
136 return activeMasterSwitches.get(dpid);
137 }
138
139 @Override
140 public OpenFlowSwitch getEqualSwitch(Dpid dpid) {
141 return activeEqualSwitches.get(dpid);
142 }
143
144 @Override
145 public void addListener(OpenFlowSwitchListener listener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700146 if (!ofSwitchListener.contains(listener)) {
147 this.ofSwitchListener.add(listener);
tom7ef8ff92014-09-17 13:08:06 -0700148 }
149 }
150
151 @Override
152 public void removeListener(OpenFlowSwitchListener listener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700153 this.ofSwitchListener.remove(listener);
tom7ef8ff92014-09-17 13:08:06 -0700154 }
155
156 @Override
157 public void addPacketListener(int priority, PacketListener listener) {
158 ofPacketListener.put(priority, listener);
159 }
160
161 @Override
162 public void removePacketListener(PacketListener listener) {
163 ofPacketListener.values().remove(listener);
164 }
165
166 @Override
alshabibeec3a062014-09-17 18:01:26 -0700167 public void addEventListener(OpenFlowEventListener listener) {
168 ofEventListener.add(listener);
169 }
170
171 @Override
172 public void removeEventListener(OpenFlowEventListener listener) {
173 ofEventListener.remove(listener);
174 }
175
176 @Override
tom7ef8ff92014-09-17 13:08:06 -0700177 public void write(Dpid dpid, OFMessage msg) {
178 this.getSwitch(dpid).sendMsg(msg);
179 }
180
181 @Override
182 public void processPacket(Dpid dpid, OFMessage msg) {
sangho6a0bb172015-02-05 12:24:48 -0800183 Collection<OFFlowStatsEntry> flowStats;
184 Collection<OFGroupStatsEntry> groupStats;
185 Collection<OFGroupDescStatsEntry> groupDescStats;
186
tom7ef8ff92014-09-17 13:08:06 -0700187 switch (msg.getType()) {
188 case PORT_STATUS:
alshabib8f1cf4a2014-09-17 14:44:48 -0700189 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700190 l.portChanged(dpid, (OFPortStatus) msg);
191 }
192 break;
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700193 case FEATURES_REPLY:
194 for (OpenFlowSwitchListener l : ofSwitchListener) {
195 l.switchChanged(dpid);
196 }
197 break;
tom7ef8ff92014-09-17 13:08:06 -0700198 case PACKET_IN:
199 OpenFlowPacketContext pktCtx = DefaultOpenFlowPacketContext
200 .packetContextFromPacketIn(this.getSwitch(dpid),
201 (OFPacketIn) msg);
202 for (PacketListener p : ofPacketListener.values()) {
203 p.handlePacket(pktCtx);
204 }
205 break;
Pavlin Radoslavov369c6432014-12-03 16:25:14 -0800206 // TODO: Consider using separate threadpool for sensitive messages.
207 // ie. Back to back error could cause us to starve.
208 case FLOW_REMOVED:
209 case ERROR:
210 executorMsgs.submit(new OFMessageHandler(dpid, msg));
211 break;
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700212 case STATS_REPLY:
213 OFStatsReply reply = (OFStatsReply) msg;
sangho6a0bb172015-02-05 12:24:48 -0800214 switch (reply.getStatsType()) {
215 case PORT_DESC:
216 for (OpenFlowSwitchListener l : ofSwitchListener) {
217 l.switchChanged(dpid);
218 }
219 break;
220 case FLOW:
221 flowStats = publishFlowStats(dpid, (OFFlowStatsReply) reply);
222 if (flowStats != null) {
223 OFFlowStatsReply.Builder rep =
224 OFFactories.getFactory(msg.getVersion()).buildFlowStatsReply();
225 rep.setEntries(Lists.newLinkedList(flowStats));
226 executorMsgs.submit(new OFMessageHandler(dpid, rep.build()));
227 }
228 break;
229 case GROUP:
230 groupStats = publishGroupStats(dpid, (OFGroupStatsReply) reply);
231 if (groupStats != null) {
232 OFGroupStatsReply.Builder rep =
233 OFFactories.getFactory(msg.getVersion()).buildGroupStatsReply();
234 rep.setEntries(Lists.newLinkedList(groupStats));
235 rep.setXid(reply.getXid());
236 executorMsgs.submit(new OFMessageHandler(dpid, rep.build()));
237 }
238 break;
239 case GROUP_DESC:
240 groupDescStats = publishGroupDescStats(dpid,
241 (OFGroupDescStatsReply) reply);
242 if (groupDescStats != null) {
243 OFGroupDescStatsReply.Builder rep =
244 OFFactories.getFactory(msg.getVersion()).buildGroupDescStatsReply();
245 rep.setEntries(Lists.newLinkedList(groupDescStats));
246 rep.setXid(reply.getXid());
247 executorMsgs.submit(new OFMessageHandler(dpid, rep.build()));
248 }
249 break;
250 default:
251 log.warn("Unsupported stats type : {}", reply.getStatsType());
alshabib64def642014-12-02 23:27:37 -0800252 }
253 break;
alshabib8f1cf4a2014-09-17 14:44:48 -0700254 case BARRIER_REPLY:
Pavlin Radoslavov369c6432014-12-03 16:25:14 -0800255 executorBarrier.submit(new OFMessageHandler(dpid, msg));
alshabib8f1cf4a2014-09-17 14:44:48 -0700256 break;
Marc De Leenheer631ffce2014-10-28 16:29:07 -0700257 case EXPERIMENTER:
258 // Handle optical port stats
259 if (((OFExperimenter) msg).getExperimenter() == 0x748771) {
260 OFCircuitPortStatus circuitPortStatus = (OFCircuitPortStatus) msg;
261 OFPortStatus.Builder portStatus = this.getSwitch(dpid).factory().buildPortStatus();
262 OFPortDesc.Builder portDesc = this.getSwitch(dpid).factory().buildPortDesc();
263 portDesc.setPortNo(circuitPortStatus.getPortNo())
264 .setHwAddr(circuitPortStatus.getHwAddr())
265 .setName(circuitPortStatus.getName())
266 .setConfig(circuitPortStatus.getConfig())
267 .setState(circuitPortStatus.getState());
268 portStatus.setReason(circuitPortStatus.getReason()).setDesc(portDesc.build());
269 for (OpenFlowSwitchListener l : ofSwitchListener) {
270 l.portChanged(dpid, portStatus.build());
271 }
272 } else {
273 log.warn("Handling experimenter type {} not yet implemented",
274 ((OFExperimenter) msg).getExperimenter(), msg);
275 }
276 break;
tom7ef8ff92014-09-17 13:08:06 -0700277 default:
278 log.warn("Handling message type {} not yet implemented {}",
279 msg.getType(), msg);
280 }
281 }
282
sangho6a0bb172015-02-05 12:24:48 -0800283 private synchronized Collection<OFFlowStatsEntry> publishFlowStats(Dpid dpid,
284 OFFlowStatsReply reply) {
alshabib64def642014-12-02 23:27:37 -0800285 //TODO: Get rid of synchronized
sangho6a0bb172015-02-05 12:24:48 -0800286 fullFlowStats.putAll(dpid, reply.getEntries());
alshabib64def642014-12-02 23:27:37 -0800287 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
sangho6a0bb172015-02-05 12:24:48 -0800288 return fullFlowStats.removeAll(dpid);
289 }
290 return null;
291 }
292
293 private synchronized Collection<OFGroupStatsEntry> publishGroupStats(Dpid dpid,
294 OFGroupStatsReply reply) {
295 //TODO: Get rid of synchronized
296 fullGroupStats.putAll(dpid, reply.getEntries());
297 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
298 return fullGroupStats.removeAll(dpid);
299 }
300 return null;
301 }
302
303 private synchronized Collection<OFGroupDescStatsEntry> publishGroupDescStats(Dpid dpid,
304 OFGroupDescStatsReply reply) {
305 //TODO: Get rid of synchronized
306 fullGroupDescStats.putAll(dpid, reply.getEntries());
307 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
308 return fullGroupDescStats.removeAll(dpid);
alshabib64def642014-12-02 23:27:37 -0800309 }
310 return null;
311 }
312
tom7ef8ff92014-09-17 13:08:06 -0700313 @Override
314 public void setRole(Dpid dpid, RoleState role) {
Yuta HIGUCHI79cbd1c2014-10-02 16:57:57 -0700315 final OpenFlowSwitch sw = getSwitch(dpid);
316 if (sw == null) {
317 log.debug("Switch not connected. Ignoring setRole({}, {})", dpid, role);
318 return;
319 }
320 sw.setRole(role);
tom7ef8ff92014-09-17 13:08:06 -0700321 }
322
323 /**
324 * Implementation of an OpenFlow Agent which is responsible for
325 * keeping track of connected switches and the state in which
326 * they are.
327 */
328 public class OpenFlowSwitchAgent implements OpenFlowAgent {
329
330 private final Logger log = LoggerFactory.getLogger(OpenFlowSwitchAgent.class);
331 private final Lock switchLock = new ReentrantLock();
332
333 @Override
334 public boolean addConnectedSwitch(Dpid dpid, OpenFlowSwitch sw) {
alshabib9eab22f2014-10-20 17:17:31 -0700335
tom7ef8ff92014-09-17 13:08:06 -0700336 if (connectedSwitches.get(dpid) != null) {
337 log.error("Trying to add connectedSwitch but found a previous "
338 + "value for dpid: {}", dpid);
339 return false;
340 } else {
Yuta HIGUCHIeb3f30b2014-10-22 11:34:49 -0700341 log.info("Added switch {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700342 connectedSwitches.put(dpid, sw);
alshabib8f1cf4a2014-09-17 14:44:48 -0700343 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700344 l.switchAdded(dpid);
345 }
346 return true;
347 }
348 }
349
350 @Override
351 public boolean validActivation(Dpid dpid) {
352 if (connectedSwitches.get(dpid) == null) {
353 log.error("Trying to activate switch but is not in "
354 + "connected switches: dpid {}. Aborting ..",
355 dpid);
356 return false;
357 }
358 if (activeMasterSwitches.get(dpid) != null ||
359 activeEqualSwitches.get(dpid) != null) {
360 log.error("Trying to activate switch but it is already "
361 + "activated: dpid {}. Found in activeMaster: {} "
362 + "Found in activeEqual: {}. Aborting ..", new Object[]{
363 dpid,
364 (activeMasterSwitches.get(dpid) == null) ? 'N' : 'Y',
365 (activeEqualSwitches.get(dpid) == null) ? 'N' : 'Y'});
366 return false;
367 }
368 return true;
369 }
370
371
372 @Override
373 public boolean addActivatedMasterSwitch(Dpid dpid, OpenFlowSwitch sw) {
374 switchLock.lock();
375 try {
376 if (!validActivation(dpid)) {
377 return false;
378 }
379 activeMasterSwitches.put(dpid, sw);
380 return true;
381 } finally {
382 switchLock.unlock();
383 }
384 }
385
386 @Override
387 public boolean addActivatedEqualSwitch(Dpid dpid, OpenFlowSwitch sw) {
388 switchLock.lock();
389 try {
390 if (!validActivation(dpid)) {
391 return false;
392 }
393 activeEqualSwitches.put(dpid, sw);
394 log.info("Added Activated EQUAL Switch {}", dpid);
395 return true;
396 } finally {
397 switchLock.unlock();
398 }
399 }
400
401 @Override
402 public void transitionToMasterSwitch(Dpid dpid) {
403 switchLock.lock();
404 try {
405 if (activeMasterSwitches.containsKey(dpid)) {
406 return;
407 }
408 OpenFlowSwitch sw = activeEqualSwitches.remove(dpid);
409 if (sw == null) {
410 sw = getSwitch(dpid);
411 if (sw == null) {
412 log.error("Transition to master called on sw {}, but switch "
413 + "was not found in controller-cache", dpid);
414 return;
415 }
416 }
417 log.info("Transitioned switch {} to MASTER", dpid);
418 activeMasterSwitches.put(dpid, sw);
419 } finally {
420 switchLock.unlock();
421 }
422 }
423
424
425 @Override
426 public void transitionToEqualSwitch(Dpid dpid) {
427 switchLock.lock();
428 try {
429 if (activeEqualSwitches.containsKey(dpid)) {
430 return;
431 }
432 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
433 if (sw == null) {
434 sw = getSwitch(dpid);
435 if (sw == null) {
436 log.error("Transition to equal called on sw {}, but switch "
437 + "was not found in controller-cache", dpid);
438 return;
439 }
440 }
441 log.info("Transitioned switch {} to EQUAL", dpid);
442 activeEqualSwitches.put(dpid, sw);
443 } finally {
444 switchLock.unlock();
445 }
446
447 }
448
449 @Override
450 public void removeConnectedSwitch(Dpid dpid) {
451 connectedSwitches.remove(dpid);
452 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
453 if (sw == null) {
Ayaka Koshibec4047702014-10-07 14:43:52 -0700454 log.warn("sw was null for {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700455 sw = activeEqualSwitches.remove(dpid);
456 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700457 for (OpenFlowSwitchListener l : ofSwitchListener) {
Ayaka Koshibec4047702014-10-07 14:43:52 -0700458 log.warn("removal for {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700459 l.switchRemoved(dpid);
460 }
461 }
462
463 @Override
464 public void processMessage(Dpid dpid, OFMessage m) {
465 processPacket(dpid, m);
466 }
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700467
468 @Override
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700469 public void returnRoleReply(Dpid dpid, RoleState requested, RoleState response) {
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700470 for (OpenFlowSwitchListener l : ofSwitchListener) {
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700471 l.receivedRoleReply(dpid, requested, response);
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700472 }
473 }
tom7ef8ff92014-09-17 13:08:06 -0700474 }
475
alshabib8f1cf4a2014-09-17 14:44:48 -0700476 private final class OFMessageHandler implements Runnable {
477
478 private final OFMessage msg;
479 private final Dpid dpid;
480
481 public OFMessageHandler(Dpid dpid, OFMessage msg) {
482 this.msg = msg;
483 this.dpid = dpid;
484 }
485
486 @Override
487 public void run() {
alshabibeec3a062014-09-17 18:01:26 -0700488 for (OpenFlowEventListener listener : ofEventListener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700489 listener.handleMessage(dpid, msg);
490 }
491 }
492
493 }
494
tom7ef8ff92014-09-17 13:08:06 -0700495}