blob: 51c746f5c4748ed9346681ab67d84c8349289e73 [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;
tom7ef8ff92014-09-17 13:08:06 -070049import org.projectfloodlight.openflow.protocol.OFMessage;
50import org.projectfloodlight.openflow.protocol.OFPacketIn;
Marc De Leenheer631ffce2014-10-28 16:29:07 -070051import org.projectfloodlight.openflow.protocol.OFPortDesc;
tom7ef8ff92014-09-17 13:08:06 -070052import org.projectfloodlight.openflow.protocol.OFPortStatus;
Ayaka Koshibe38594c22014-10-22 13:36:12 -070053import org.projectfloodlight.openflow.protocol.OFStatsReply;
alshabib64def642014-12-02 23:27:37 -080054import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
Ayaka Koshibe38594c22014-10-22 13:36:12 -070055import org.projectfloodlight.openflow.protocol.OFStatsType;
tom7ef8ff92014-09-17 13:08:06 -070056import org.slf4j.Logger;
57import org.slf4j.LoggerFactory;
58
59import com.google.common.collect.ArrayListMultimap;
60import com.google.common.collect.Multimap;
alshabibeec3a062014-09-17 18:01:26 -070061import com.google.common.collect.Sets;
tom7ef8ff92014-09-17 13:08:06 -070062
63@Component(immediate = true)
64@Service
65public class OpenFlowControllerImpl implements OpenFlowController {
66
67 private static final Logger log =
68 LoggerFactory.getLogger(OpenFlowControllerImpl.class);
69
Pavlin Radoslavov369c6432014-12-03 16:25:14 -080070 private final ExecutorService executorMsgs =
71 Executors.newFixedThreadPool(32,
72 namedThreads("of-event-stats-%d"));
73
74 private final ExecutorService executorBarrier =
75 Executors.newFixedThreadPool(4,
76 namedThreads("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
alshabib64def642014-12-02 23:27:37 -080093 protected Multimap<Dpid, OFFlowStatsEntry> fullStats =
94 ArrayListMultimap.create();
95
tom7ef8ff92014-09-17 13:08:06 -070096 private final Controller ctrl = new Controller();
97
98 @Activate
99 public void activate() {
100 ctrl.start(agent);
101 }
102
103 @Deactivate
104 public void deactivate() {
105 ctrl.stop();
106 }
107
108 @Override
109 public Iterable<OpenFlowSwitch> getSwitches() {
110 return connectedSwitches.values();
111 }
112
113 @Override
114 public Iterable<OpenFlowSwitch> getMasterSwitches() {
115 return activeMasterSwitches.values();
116 }
117
118 @Override
119 public Iterable<OpenFlowSwitch> getEqualSwitches() {
120 return activeEqualSwitches.values();
121 }
122
123 @Override
124 public OpenFlowSwitch getSwitch(Dpid dpid) {
125 return connectedSwitches.get(dpid);
126 }
127
128 @Override
129 public OpenFlowSwitch getMasterSwitch(Dpid dpid) {
130 return activeMasterSwitches.get(dpid);
131 }
132
133 @Override
134 public OpenFlowSwitch getEqualSwitch(Dpid dpid) {
135 return activeEqualSwitches.get(dpid);
136 }
137
138 @Override
139 public void addListener(OpenFlowSwitchListener listener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700140 if (!ofSwitchListener.contains(listener)) {
141 this.ofSwitchListener.add(listener);
tom7ef8ff92014-09-17 13:08:06 -0700142 }
143 }
144
145 @Override
146 public void removeListener(OpenFlowSwitchListener listener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700147 this.ofSwitchListener.remove(listener);
tom7ef8ff92014-09-17 13:08:06 -0700148 }
149
150 @Override
151 public void addPacketListener(int priority, PacketListener listener) {
152 ofPacketListener.put(priority, listener);
153 }
154
155 @Override
156 public void removePacketListener(PacketListener listener) {
157 ofPacketListener.values().remove(listener);
158 }
159
160 @Override
alshabibeec3a062014-09-17 18:01:26 -0700161 public void addEventListener(OpenFlowEventListener listener) {
162 ofEventListener.add(listener);
163 }
164
165 @Override
166 public void removeEventListener(OpenFlowEventListener listener) {
167 ofEventListener.remove(listener);
168 }
169
170 @Override
tom7ef8ff92014-09-17 13:08:06 -0700171 public void write(Dpid dpid, OFMessage msg) {
172 this.getSwitch(dpid).sendMsg(msg);
173 }
174
175 @Override
176 public void processPacket(Dpid dpid, OFMessage msg) {
alshabib64def642014-12-02 23:27:37 -0800177 Collection<OFFlowStatsEntry> stats;
tom7ef8ff92014-09-17 13:08:06 -0700178 switch (msg.getType()) {
179 case PORT_STATUS:
alshabib8f1cf4a2014-09-17 14:44:48 -0700180 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700181 l.portChanged(dpid, (OFPortStatus) msg);
182 }
183 break;
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700184 case FEATURES_REPLY:
185 for (OpenFlowSwitchListener l : ofSwitchListener) {
186 l.switchChanged(dpid);
187 }
188 break;
tom7ef8ff92014-09-17 13:08:06 -0700189 case PACKET_IN:
190 OpenFlowPacketContext pktCtx = DefaultOpenFlowPacketContext
191 .packetContextFromPacketIn(this.getSwitch(dpid),
192 (OFPacketIn) msg);
193 for (PacketListener p : ofPacketListener.values()) {
194 p.handlePacket(pktCtx);
195 }
196 break;
Pavlin Radoslavov369c6432014-12-03 16:25:14 -0800197 // TODO: Consider using separate threadpool for sensitive messages.
198 // ie. Back to back error could cause us to starve.
199 case FLOW_REMOVED:
200 case ERROR:
201 executorMsgs.submit(new OFMessageHandler(dpid, msg));
202 break;
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700203 case STATS_REPLY:
204 OFStatsReply reply = (OFStatsReply) msg;
205 if (reply.getStatsType().equals(OFStatsType.PORT_DESC)) {
206 for (OpenFlowSwitchListener l : ofSwitchListener) {
207 l.switchChanged(dpid);
208 }
209 }
alshabib64def642014-12-02 23:27:37 -0800210 stats = publishStats(dpid, reply);
211 if (stats != null) {
212 OFFlowStatsReply.Builder rep =
213 OFFactories.getFactory(msg.getVersion()).buildFlowStatsReply();
214 rep.setEntries(Lists.newLinkedList(stats));
Pavlin Radoslavov369c6432014-12-03 16:25:14 -0800215 executorMsgs.submit(new OFMessageHandler(dpid, rep.build()));
alshabib64def642014-12-02 23:27:37 -0800216 }
217 break;
alshabib8f1cf4a2014-09-17 14:44:48 -0700218 case BARRIER_REPLY:
Pavlin Radoslavov369c6432014-12-03 16:25:14 -0800219 executorBarrier.submit(new OFMessageHandler(dpid, msg));
alshabib8f1cf4a2014-09-17 14:44:48 -0700220 break;
Marc De Leenheer631ffce2014-10-28 16:29:07 -0700221 case EXPERIMENTER:
222 // Handle optical port stats
223 if (((OFExperimenter) msg).getExperimenter() == 0x748771) {
224 OFCircuitPortStatus circuitPortStatus = (OFCircuitPortStatus) msg;
225 OFPortStatus.Builder portStatus = this.getSwitch(dpid).factory().buildPortStatus();
226 OFPortDesc.Builder portDesc = this.getSwitch(dpid).factory().buildPortDesc();
227 portDesc.setPortNo(circuitPortStatus.getPortNo())
228 .setHwAddr(circuitPortStatus.getHwAddr())
229 .setName(circuitPortStatus.getName())
230 .setConfig(circuitPortStatus.getConfig())
231 .setState(circuitPortStatus.getState());
232 portStatus.setReason(circuitPortStatus.getReason()).setDesc(portDesc.build());
233 for (OpenFlowSwitchListener l : ofSwitchListener) {
234 l.portChanged(dpid, portStatus.build());
235 }
236 } else {
237 log.warn("Handling experimenter type {} not yet implemented",
238 ((OFExperimenter) msg).getExperimenter(), msg);
239 }
240 break;
tom7ef8ff92014-09-17 13:08:06 -0700241 default:
242 log.warn("Handling message type {} not yet implemented {}",
243 msg.getType(), msg);
244 }
245 }
246
alshabib64def642014-12-02 23:27:37 -0800247 private synchronized Collection<OFFlowStatsEntry> publishStats(Dpid dpid,
248 OFStatsReply reply) {
249 //TODO: Get rid of synchronized
250 if (reply.getStatsType() != OFStatsType.FLOW) {
251 return null;
252 }
253 final OFFlowStatsReply replies = (OFFlowStatsReply) reply;
254 fullStats.putAll(dpid, replies.getEntries());
255 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
256 return fullStats.removeAll(dpid);
257 }
258 return null;
259 }
260
tom7ef8ff92014-09-17 13:08:06 -0700261 @Override
262 public void setRole(Dpid dpid, RoleState role) {
Yuta HIGUCHI79cbd1c2014-10-02 16:57:57 -0700263 final OpenFlowSwitch sw = getSwitch(dpid);
264 if (sw == null) {
265 log.debug("Switch not connected. Ignoring setRole({}, {})", dpid, role);
266 return;
267 }
268 sw.setRole(role);
tom7ef8ff92014-09-17 13:08:06 -0700269 }
270
271 /**
272 * Implementation of an OpenFlow Agent which is responsible for
273 * keeping track of connected switches and the state in which
274 * they are.
275 */
276 public class OpenFlowSwitchAgent implements OpenFlowAgent {
277
278 private final Logger log = LoggerFactory.getLogger(OpenFlowSwitchAgent.class);
279 private final Lock switchLock = new ReentrantLock();
280
281 @Override
282 public boolean addConnectedSwitch(Dpid dpid, OpenFlowSwitch sw) {
alshabib9eab22f2014-10-20 17:17:31 -0700283
tom7ef8ff92014-09-17 13:08:06 -0700284 if (connectedSwitches.get(dpid) != null) {
285 log.error("Trying to add connectedSwitch but found a previous "
286 + "value for dpid: {}", dpid);
287 return false;
288 } else {
Yuta HIGUCHIeb3f30b2014-10-22 11:34:49 -0700289 log.info("Added switch {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700290 connectedSwitches.put(dpid, sw);
alshabib8f1cf4a2014-09-17 14:44:48 -0700291 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700292 l.switchAdded(dpid);
293 }
294 return true;
295 }
296 }
297
298 @Override
299 public boolean validActivation(Dpid dpid) {
300 if (connectedSwitches.get(dpid) == null) {
301 log.error("Trying to activate switch but is not in "
302 + "connected switches: dpid {}. Aborting ..",
303 dpid);
304 return false;
305 }
306 if (activeMasterSwitches.get(dpid) != null ||
307 activeEqualSwitches.get(dpid) != null) {
308 log.error("Trying to activate switch but it is already "
309 + "activated: dpid {}. Found in activeMaster: {} "
310 + "Found in activeEqual: {}. Aborting ..", new Object[]{
311 dpid,
312 (activeMasterSwitches.get(dpid) == null) ? 'N' : 'Y',
313 (activeEqualSwitches.get(dpid) == null) ? 'N' : 'Y'});
314 return false;
315 }
316 return true;
317 }
318
319
320 @Override
321 public boolean addActivatedMasterSwitch(Dpid dpid, OpenFlowSwitch sw) {
322 switchLock.lock();
323 try {
324 if (!validActivation(dpid)) {
325 return false;
326 }
327 activeMasterSwitches.put(dpid, sw);
328 return true;
329 } finally {
330 switchLock.unlock();
331 }
332 }
333
334 @Override
335 public boolean addActivatedEqualSwitch(Dpid dpid, OpenFlowSwitch sw) {
336 switchLock.lock();
337 try {
338 if (!validActivation(dpid)) {
339 return false;
340 }
341 activeEqualSwitches.put(dpid, sw);
342 log.info("Added Activated EQUAL Switch {}", dpid);
343 return true;
344 } finally {
345 switchLock.unlock();
346 }
347 }
348
349 @Override
350 public void transitionToMasterSwitch(Dpid dpid) {
351 switchLock.lock();
352 try {
353 if (activeMasterSwitches.containsKey(dpid)) {
354 return;
355 }
356 OpenFlowSwitch sw = activeEqualSwitches.remove(dpid);
357 if (sw == null) {
358 sw = getSwitch(dpid);
359 if (sw == null) {
360 log.error("Transition to master called on sw {}, but switch "
361 + "was not found in controller-cache", dpid);
362 return;
363 }
364 }
365 log.info("Transitioned switch {} to MASTER", dpid);
366 activeMasterSwitches.put(dpid, sw);
367 } finally {
368 switchLock.unlock();
369 }
370 }
371
372
373 @Override
374 public void transitionToEqualSwitch(Dpid dpid) {
375 switchLock.lock();
376 try {
377 if (activeEqualSwitches.containsKey(dpid)) {
378 return;
379 }
380 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
381 if (sw == null) {
382 sw = getSwitch(dpid);
383 if (sw == null) {
384 log.error("Transition to equal called on sw {}, but switch "
385 + "was not found in controller-cache", dpid);
386 return;
387 }
388 }
389 log.info("Transitioned switch {} to EQUAL", dpid);
390 activeEqualSwitches.put(dpid, sw);
391 } finally {
392 switchLock.unlock();
393 }
394
395 }
396
397 @Override
398 public void removeConnectedSwitch(Dpid dpid) {
399 connectedSwitches.remove(dpid);
400 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
401 if (sw == null) {
Ayaka Koshibec4047702014-10-07 14:43:52 -0700402 log.warn("sw was null for {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700403 sw = activeEqualSwitches.remove(dpid);
404 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700405 for (OpenFlowSwitchListener l : ofSwitchListener) {
Ayaka Koshibec4047702014-10-07 14:43:52 -0700406 log.warn("removal for {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700407 l.switchRemoved(dpid);
408 }
409 }
410
411 @Override
412 public void processMessage(Dpid dpid, OFMessage m) {
413 processPacket(dpid, m);
414 }
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700415
416 @Override
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700417 public void returnRoleReply(Dpid dpid, RoleState requested, RoleState response) {
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700418 for (OpenFlowSwitchListener l : ofSwitchListener) {
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700419 l.receivedRoleReply(dpid, requested, response);
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700420 }
421 }
tom7ef8ff92014-09-17 13:08:06 -0700422 }
423
alshabib8f1cf4a2014-09-17 14:44:48 -0700424 private final class OFMessageHandler implements Runnable {
425
426 private final OFMessage msg;
427 private final Dpid dpid;
428
429 public OFMessageHandler(Dpid dpid, OFMessage msg) {
430 this.msg = msg;
431 this.dpid = dpid;
432 }
433
434 @Override
435 public void run() {
alshabibeec3a062014-09-17 18:01:26 -0700436 for (OpenFlowEventListener listener : ofEventListener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700437 listener.handleMessage(dpid, msg);
438 }
439 }
440
441 }
442
tom7ef8ff92014-09-17 13:08:06 -0700443}