blob: edd2f3a054c7590e593a76e9bb3ef2d901c72c0f [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
alshabib8f1cf4a2014-09-17 14:44:48 -070070 private final ExecutorService executor = Executors.newFixedThreadPool(16,
alshabibeec3a062014-09-17 18:01:26 -070071 namedThreads("of-event-%d"));
alshabib8f1cf4a2014-09-17 14:44:48 -070072
tom7ef8ff92014-09-17 13:08:06 -070073 protected ConcurrentHashMap<Dpid, OpenFlowSwitch> connectedSwitches =
74 new ConcurrentHashMap<Dpid, OpenFlowSwitch>();
75 protected ConcurrentHashMap<Dpid, OpenFlowSwitch> activeMasterSwitches =
76 new ConcurrentHashMap<Dpid, OpenFlowSwitch>();
77 protected ConcurrentHashMap<Dpid, OpenFlowSwitch> activeEqualSwitches =
78 new ConcurrentHashMap<Dpid, OpenFlowSwitch>();
79
80 protected OpenFlowSwitchAgent agent = new OpenFlowSwitchAgent();
alshabib8f1cf4a2014-09-17 14:44:48 -070081 protected Set<OpenFlowSwitchListener> ofSwitchListener = new HashSet<>();
tom7ef8ff92014-09-17 13:08:06 -070082
83 protected Multimap<Integer, PacketListener> ofPacketListener =
84 ArrayListMultimap.create();
85
alshabibeec3a062014-09-17 18:01:26 -070086 protected Set<OpenFlowEventListener> ofEventListener = Sets.newHashSet();
tom7ef8ff92014-09-17 13:08:06 -070087
alshabib64def642014-12-02 23:27:37 -080088 protected Multimap<Dpid, OFFlowStatsEntry> fullStats =
89 ArrayListMultimap.create();
90
tom7ef8ff92014-09-17 13:08:06 -070091 private final Controller ctrl = new Controller();
92
93 @Activate
94 public void activate() {
95 ctrl.start(agent);
96 }
97
98 @Deactivate
99 public void deactivate() {
100 ctrl.stop();
101 }
102
103 @Override
104 public Iterable<OpenFlowSwitch> getSwitches() {
105 return connectedSwitches.values();
106 }
107
108 @Override
109 public Iterable<OpenFlowSwitch> getMasterSwitches() {
110 return activeMasterSwitches.values();
111 }
112
113 @Override
114 public Iterable<OpenFlowSwitch> getEqualSwitches() {
115 return activeEqualSwitches.values();
116 }
117
118 @Override
119 public OpenFlowSwitch getSwitch(Dpid dpid) {
120 return connectedSwitches.get(dpid);
121 }
122
123 @Override
124 public OpenFlowSwitch getMasterSwitch(Dpid dpid) {
125 return activeMasterSwitches.get(dpid);
126 }
127
128 @Override
129 public OpenFlowSwitch getEqualSwitch(Dpid dpid) {
130 return activeEqualSwitches.get(dpid);
131 }
132
133 @Override
134 public void addListener(OpenFlowSwitchListener listener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700135 if (!ofSwitchListener.contains(listener)) {
136 this.ofSwitchListener.add(listener);
tom7ef8ff92014-09-17 13:08:06 -0700137 }
138 }
139
140 @Override
141 public void removeListener(OpenFlowSwitchListener listener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700142 this.ofSwitchListener.remove(listener);
tom7ef8ff92014-09-17 13:08:06 -0700143 }
144
145 @Override
146 public void addPacketListener(int priority, PacketListener listener) {
147 ofPacketListener.put(priority, listener);
148 }
149
150 @Override
151 public void removePacketListener(PacketListener listener) {
152 ofPacketListener.values().remove(listener);
153 }
154
155 @Override
alshabibeec3a062014-09-17 18:01:26 -0700156 public void addEventListener(OpenFlowEventListener listener) {
157 ofEventListener.add(listener);
158 }
159
160 @Override
161 public void removeEventListener(OpenFlowEventListener listener) {
162 ofEventListener.remove(listener);
163 }
164
165 @Override
tom7ef8ff92014-09-17 13:08:06 -0700166 public void write(Dpid dpid, OFMessage msg) {
167 this.getSwitch(dpid).sendMsg(msg);
168 }
169
170 @Override
171 public void processPacket(Dpid dpid, OFMessage msg) {
alshabib64def642014-12-02 23:27:37 -0800172 Collection<OFFlowStatsEntry> stats;
tom7ef8ff92014-09-17 13:08:06 -0700173 switch (msg.getType()) {
174 case PORT_STATUS:
alshabib8f1cf4a2014-09-17 14:44:48 -0700175 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700176 l.portChanged(dpid, (OFPortStatus) msg);
177 }
178 break;
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700179 case FEATURES_REPLY:
180 for (OpenFlowSwitchListener l : ofSwitchListener) {
181 l.switchChanged(dpid);
182 }
183 break;
tom7ef8ff92014-09-17 13:08:06 -0700184 case PACKET_IN:
185 OpenFlowPacketContext pktCtx = DefaultOpenFlowPacketContext
186 .packetContextFromPacketIn(this.getSwitch(dpid),
187 (OFPacketIn) msg);
188 for (PacketListener p : ofPacketListener.values()) {
189 p.handlePacket(pktCtx);
190 }
191 break;
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700192 case STATS_REPLY:
193 OFStatsReply reply = (OFStatsReply) msg;
194 if (reply.getStatsType().equals(OFStatsType.PORT_DESC)) {
195 for (OpenFlowSwitchListener l : ofSwitchListener) {
196 l.switchChanged(dpid);
197 }
198 }
alshabib64def642014-12-02 23:27:37 -0800199 stats = publishStats(dpid, reply);
200 if (stats != null) {
201 OFFlowStatsReply.Builder rep =
202 OFFactories.getFactory(msg.getVersion()).buildFlowStatsReply();
203 rep.setEntries(Lists.newLinkedList(stats));
204 executor.submit(new OFMessageHandler(dpid, rep.build()));
205 }
206 break;
207
alshabib8f1cf4a2014-09-17 14:44:48 -0700208 case FLOW_REMOVED:
209 case ERROR:
alshabib8f1cf4a2014-09-17 14:44:48 -0700210 case BARRIER_REPLY:
211 executor.submit(new OFMessageHandler(dpid, msg));
212 break;
Marc De Leenheer631ffce2014-10-28 16:29:07 -0700213 case EXPERIMENTER:
214 // Handle optical port stats
215 if (((OFExperimenter) msg).getExperimenter() == 0x748771) {
216 OFCircuitPortStatus circuitPortStatus = (OFCircuitPortStatus) msg;
217 OFPortStatus.Builder portStatus = this.getSwitch(dpid).factory().buildPortStatus();
218 OFPortDesc.Builder portDesc = this.getSwitch(dpid).factory().buildPortDesc();
219 portDesc.setPortNo(circuitPortStatus.getPortNo())
220 .setHwAddr(circuitPortStatus.getHwAddr())
221 .setName(circuitPortStatus.getName())
222 .setConfig(circuitPortStatus.getConfig())
223 .setState(circuitPortStatus.getState());
224 portStatus.setReason(circuitPortStatus.getReason()).setDesc(portDesc.build());
225 for (OpenFlowSwitchListener l : ofSwitchListener) {
226 l.portChanged(dpid, portStatus.build());
227 }
228 } else {
229 log.warn("Handling experimenter type {} not yet implemented",
230 ((OFExperimenter) msg).getExperimenter(), msg);
231 }
232 break;
tom7ef8ff92014-09-17 13:08:06 -0700233 default:
234 log.warn("Handling message type {} not yet implemented {}",
235 msg.getType(), msg);
236 }
237 }
238
alshabib64def642014-12-02 23:27:37 -0800239 private synchronized Collection<OFFlowStatsEntry> publishStats(Dpid dpid,
240 OFStatsReply reply) {
241 //TODO: Get rid of synchronized
242 if (reply.getStatsType() != OFStatsType.FLOW) {
243 return null;
244 }
245 final OFFlowStatsReply replies = (OFFlowStatsReply) reply;
246 fullStats.putAll(dpid, replies.getEntries());
247 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
248 return fullStats.removeAll(dpid);
249 }
250 return null;
251 }
252
tom7ef8ff92014-09-17 13:08:06 -0700253 @Override
254 public void setRole(Dpid dpid, RoleState role) {
Yuta HIGUCHI79cbd1c2014-10-02 16:57:57 -0700255 final OpenFlowSwitch sw = getSwitch(dpid);
256 if (sw == null) {
257 log.debug("Switch not connected. Ignoring setRole({}, {})", dpid, role);
258 return;
259 }
260 sw.setRole(role);
tom7ef8ff92014-09-17 13:08:06 -0700261 }
262
263 /**
264 * Implementation of an OpenFlow Agent which is responsible for
265 * keeping track of connected switches and the state in which
266 * they are.
267 */
268 public class OpenFlowSwitchAgent implements OpenFlowAgent {
269
270 private final Logger log = LoggerFactory.getLogger(OpenFlowSwitchAgent.class);
271 private final Lock switchLock = new ReentrantLock();
272
273 @Override
274 public boolean addConnectedSwitch(Dpid dpid, OpenFlowSwitch sw) {
alshabib9eab22f2014-10-20 17:17:31 -0700275
tom7ef8ff92014-09-17 13:08:06 -0700276 if (connectedSwitches.get(dpid) != null) {
277 log.error("Trying to add connectedSwitch but found a previous "
278 + "value for dpid: {}", dpid);
279 return false;
280 } else {
Yuta HIGUCHIeb3f30b2014-10-22 11:34:49 -0700281 log.info("Added switch {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700282 connectedSwitches.put(dpid, sw);
alshabib8f1cf4a2014-09-17 14:44:48 -0700283 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700284 l.switchAdded(dpid);
285 }
286 return true;
287 }
288 }
289
290 @Override
291 public boolean validActivation(Dpid dpid) {
292 if (connectedSwitches.get(dpid) == null) {
293 log.error("Trying to activate switch but is not in "
294 + "connected switches: dpid {}. Aborting ..",
295 dpid);
296 return false;
297 }
298 if (activeMasterSwitches.get(dpid) != null ||
299 activeEqualSwitches.get(dpid) != null) {
300 log.error("Trying to activate switch but it is already "
301 + "activated: dpid {}. Found in activeMaster: {} "
302 + "Found in activeEqual: {}. Aborting ..", new Object[]{
303 dpid,
304 (activeMasterSwitches.get(dpid) == null) ? 'N' : 'Y',
305 (activeEqualSwitches.get(dpid) == null) ? 'N' : 'Y'});
306 return false;
307 }
308 return true;
309 }
310
311
312 @Override
313 public boolean addActivatedMasterSwitch(Dpid dpid, OpenFlowSwitch sw) {
314 switchLock.lock();
315 try {
316 if (!validActivation(dpid)) {
317 return false;
318 }
319 activeMasterSwitches.put(dpid, sw);
320 return true;
321 } finally {
322 switchLock.unlock();
323 }
324 }
325
326 @Override
327 public boolean addActivatedEqualSwitch(Dpid dpid, OpenFlowSwitch sw) {
328 switchLock.lock();
329 try {
330 if (!validActivation(dpid)) {
331 return false;
332 }
333 activeEqualSwitches.put(dpid, sw);
334 log.info("Added Activated EQUAL Switch {}", dpid);
335 return true;
336 } finally {
337 switchLock.unlock();
338 }
339 }
340
341 @Override
342 public void transitionToMasterSwitch(Dpid dpid) {
343 switchLock.lock();
344 try {
345 if (activeMasterSwitches.containsKey(dpid)) {
346 return;
347 }
348 OpenFlowSwitch sw = activeEqualSwitches.remove(dpid);
349 if (sw == null) {
350 sw = getSwitch(dpid);
351 if (sw == null) {
352 log.error("Transition to master called on sw {}, but switch "
353 + "was not found in controller-cache", dpid);
354 return;
355 }
356 }
357 log.info("Transitioned switch {} to MASTER", dpid);
358 activeMasterSwitches.put(dpid, sw);
359 } finally {
360 switchLock.unlock();
361 }
362 }
363
364
365 @Override
366 public void transitionToEqualSwitch(Dpid dpid) {
367 switchLock.lock();
368 try {
369 if (activeEqualSwitches.containsKey(dpid)) {
370 return;
371 }
372 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
373 if (sw == null) {
374 sw = getSwitch(dpid);
375 if (sw == null) {
376 log.error("Transition to equal called on sw {}, but switch "
377 + "was not found in controller-cache", dpid);
378 return;
379 }
380 }
381 log.info("Transitioned switch {} to EQUAL", dpid);
382 activeEqualSwitches.put(dpid, sw);
383 } finally {
384 switchLock.unlock();
385 }
386
387 }
388
389 @Override
390 public void removeConnectedSwitch(Dpid dpid) {
391 connectedSwitches.remove(dpid);
392 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
393 if (sw == null) {
Ayaka Koshibec4047702014-10-07 14:43:52 -0700394 log.warn("sw was null for {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700395 sw = activeEqualSwitches.remove(dpid);
396 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700397 for (OpenFlowSwitchListener l : ofSwitchListener) {
Ayaka Koshibec4047702014-10-07 14:43:52 -0700398 log.warn("removal for {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700399 l.switchRemoved(dpid);
400 }
401 }
402
403 @Override
404 public void processMessage(Dpid dpid, OFMessage m) {
405 processPacket(dpid, m);
406 }
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700407
408 @Override
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700409 public void returnRoleReply(Dpid dpid, RoleState requested, RoleState response) {
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700410 for (OpenFlowSwitchListener l : ofSwitchListener) {
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700411 l.receivedRoleReply(dpid, requested, response);
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700412 }
413 }
tom7ef8ff92014-09-17 13:08:06 -0700414 }
415
alshabib8f1cf4a2014-09-17 14:44:48 -0700416 private final class OFMessageHandler implements Runnable {
417
418 private final OFMessage msg;
419 private final Dpid dpid;
420
421 public OFMessageHandler(Dpid dpid, OFMessage msg) {
422 this.msg = msg;
423 this.dpid = dpid;
424 }
425
426 @Override
427 public void run() {
alshabibeec3a062014-09-17 18:01:26 -0700428 for (OpenFlowEventListener listener : ofEventListener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700429 listener.handleMessage(dpid, msg);
430 }
431 }
432
433 }
434
tom7ef8ff92014-09-17 13:08:06 -0700435}