blob: ec6e9c6a4fdb9f3d22143c9f68b9a64e9af4a4a8 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
tom9c94c5b2014-09-17 13:14:42 -070019package org.onlab.onos.openflow.controller.impl;
tom7ef8ff92014-09-17 13:08:06 -070020
alshabib8f1cf4a2014-09-17 14:44:48 -070021import static org.onlab.util.Tools.namedThreads;
22
tom7ef8ff92014-09-17 13:08:06 -070023import java.util.HashSet;
24import java.util.Set;
25import java.util.concurrent.ConcurrentHashMap;
alshabib8f1cf4a2014-09-17 14:44:48 -070026import java.util.concurrent.ExecutorService;
27import java.util.concurrent.Executors;
tom7ef8ff92014-09-17 13:08:06 -070028import java.util.concurrent.locks.Lock;
29import java.util.concurrent.locks.ReentrantLock;
30
31import org.apache.felix.scr.annotations.Activate;
32import org.apache.felix.scr.annotations.Component;
33import org.apache.felix.scr.annotations.Deactivate;
34import org.apache.felix.scr.annotations.Service;
tom9c94c5b2014-09-17 13:14:42 -070035import org.onlab.onos.openflow.controller.DefaultOpenFlowPacketContext;
36import org.onlab.onos.openflow.controller.Dpid;
37import org.onlab.onos.openflow.controller.OpenFlowController;
alshabibeec3a062014-09-17 18:01:26 -070038import org.onlab.onos.openflow.controller.OpenFlowEventListener;
tom9c94c5b2014-09-17 13:14:42 -070039import org.onlab.onos.openflow.controller.OpenFlowPacketContext;
40import org.onlab.onos.openflow.controller.OpenFlowSwitch;
41import org.onlab.onos.openflow.controller.OpenFlowSwitchListener;
42import org.onlab.onos.openflow.controller.PacketListener;
43import org.onlab.onos.openflow.controller.RoleState;
44import org.onlab.onos.openflow.controller.driver.OpenFlowAgent;
tom7ef8ff92014-09-17 13:08:06 -070045import org.projectfloodlight.openflow.protocol.OFMessage;
46import org.projectfloodlight.openflow.protocol.OFPacketIn;
47import org.projectfloodlight.openflow.protocol.OFPortStatus;
Ayaka Koshibe38594c22014-10-22 13:36:12 -070048import org.projectfloodlight.openflow.protocol.OFStatsReply;
49import org.projectfloodlight.openflow.protocol.OFStatsType;
tom7ef8ff92014-09-17 13:08:06 -070050import org.slf4j.Logger;
51import org.slf4j.LoggerFactory;
52
53import com.google.common.collect.ArrayListMultimap;
54import com.google.common.collect.Multimap;
alshabibeec3a062014-09-17 18:01:26 -070055import com.google.common.collect.Sets;
tom7ef8ff92014-09-17 13:08:06 -070056
57@Component(immediate = true)
58@Service
59public class OpenFlowControllerImpl implements OpenFlowController {
60
61 private static final Logger log =
62 LoggerFactory.getLogger(OpenFlowControllerImpl.class);
63
alshabib8f1cf4a2014-09-17 14:44:48 -070064 private final ExecutorService executor = Executors.newFixedThreadPool(16,
alshabibeec3a062014-09-17 18:01:26 -070065 namedThreads("of-event-%d"));
alshabib8f1cf4a2014-09-17 14:44:48 -070066
tom7ef8ff92014-09-17 13:08:06 -070067 protected ConcurrentHashMap<Dpid, OpenFlowSwitch> connectedSwitches =
68 new ConcurrentHashMap<Dpid, OpenFlowSwitch>();
69 protected ConcurrentHashMap<Dpid, OpenFlowSwitch> activeMasterSwitches =
70 new ConcurrentHashMap<Dpid, OpenFlowSwitch>();
71 protected ConcurrentHashMap<Dpid, OpenFlowSwitch> activeEqualSwitches =
72 new ConcurrentHashMap<Dpid, OpenFlowSwitch>();
73
74 protected OpenFlowSwitchAgent agent = new OpenFlowSwitchAgent();
alshabib8f1cf4a2014-09-17 14:44:48 -070075 protected Set<OpenFlowSwitchListener> ofSwitchListener = new HashSet<>();
tom7ef8ff92014-09-17 13:08:06 -070076
77 protected Multimap<Integer, PacketListener> ofPacketListener =
78 ArrayListMultimap.create();
79
alshabibeec3a062014-09-17 18:01:26 -070080 protected Set<OpenFlowEventListener> ofEventListener = Sets.newHashSet();
tom7ef8ff92014-09-17 13:08:06 -070081
82 private final Controller ctrl = new Controller();
83
84 @Activate
85 public void activate() {
86 ctrl.start(agent);
87 }
88
89 @Deactivate
90 public void deactivate() {
91 ctrl.stop();
92 }
93
94 @Override
95 public Iterable<OpenFlowSwitch> getSwitches() {
96 return connectedSwitches.values();
97 }
98
99 @Override
100 public Iterable<OpenFlowSwitch> getMasterSwitches() {
101 return activeMasterSwitches.values();
102 }
103
104 @Override
105 public Iterable<OpenFlowSwitch> getEqualSwitches() {
106 return activeEqualSwitches.values();
107 }
108
109 @Override
110 public OpenFlowSwitch getSwitch(Dpid dpid) {
111 return connectedSwitches.get(dpid);
112 }
113
114 @Override
115 public OpenFlowSwitch getMasterSwitch(Dpid dpid) {
116 return activeMasterSwitches.get(dpid);
117 }
118
119 @Override
120 public OpenFlowSwitch getEqualSwitch(Dpid dpid) {
121 return activeEqualSwitches.get(dpid);
122 }
123
124 @Override
125 public void addListener(OpenFlowSwitchListener listener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700126 if (!ofSwitchListener.contains(listener)) {
127 this.ofSwitchListener.add(listener);
tom7ef8ff92014-09-17 13:08:06 -0700128 }
129 }
130
131 @Override
132 public void removeListener(OpenFlowSwitchListener listener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700133 this.ofSwitchListener.remove(listener);
tom7ef8ff92014-09-17 13:08:06 -0700134 }
135
136 @Override
137 public void addPacketListener(int priority, PacketListener listener) {
138 ofPacketListener.put(priority, listener);
139 }
140
141 @Override
142 public void removePacketListener(PacketListener listener) {
143 ofPacketListener.values().remove(listener);
144 }
145
146 @Override
alshabibeec3a062014-09-17 18:01:26 -0700147 public void addEventListener(OpenFlowEventListener listener) {
148 ofEventListener.add(listener);
149 }
150
151 @Override
152 public void removeEventListener(OpenFlowEventListener listener) {
153 ofEventListener.remove(listener);
154 }
155
156 @Override
tom7ef8ff92014-09-17 13:08:06 -0700157 public void write(Dpid dpid, OFMessage msg) {
158 this.getSwitch(dpid).sendMsg(msg);
159 }
160
161 @Override
162 public void processPacket(Dpid dpid, OFMessage msg) {
163 switch (msg.getType()) {
164 case PORT_STATUS:
alshabib8f1cf4a2014-09-17 14:44:48 -0700165 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700166 l.portChanged(dpid, (OFPortStatus) msg);
167 }
168 break;
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700169 case FEATURES_REPLY:
170 for (OpenFlowSwitchListener l : ofSwitchListener) {
171 l.switchChanged(dpid);
172 }
173 break;
tom7ef8ff92014-09-17 13:08:06 -0700174 case PACKET_IN:
175 OpenFlowPacketContext pktCtx = DefaultOpenFlowPacketContext
176 .packetContextFromPacketIn(this.getSwitch(dpid),
177 (OFPacketIn) msg);
178 for (PacketListener p : ofPacketListener.values()) {
179 p.handlePacket(pktCtx);
180 }
181 break;
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700182 case STATS_REPLY:
183 OFStatsReply reply = (OFStatsReply) msg;
184 if (reply.getStatsType().equals(OFStatsType.PORT_DESC)) {
185 for (OpenFlowSwitchListener l : ofSwitchListener) {
186 l.switchChanged(dpid);
187 }
188 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700189 case FLOW_REMOVED:
190 case ERROR:
alshabib8f1cf4a2014-09-17 14:44:48 -0700191 case BARRIER_REPLY:
192 executor.submit(new OFMessageHandler(dpid, msg));
193 break;
tom7ef8ff92014-09-17 13:08:06 -0700194 default:
195 log.warn("Handling message type {} not yet implemented {}",
196 msg.getType(), msg);
197 }
198 }
199
200 @Override
201 public void setRole(Dpid dpid, RoleState role) {
Yuta HIGUCHI79cbd1c2014-10-02 16:57:57 -0700202 final OpenFlowSwitch sw = getSwitch(dpid);
203 if (sw == null) {
204 log.debug("Switch not connected. Ignoring setRole({}, {})", dpid, role);
205 return;
206 }
207 sw.setRole(role);
tom7ef8ff92014-09-17 13:08:06 -0700208 }
209
210 /**
211 * Implementation of an OpenFlow Agent which is responsible for
212 * keeping track of connected switches and the state in which
213 * they are.
214 */
215 public class OpenFlowSwitchAgent implements OpenFlowAgent {
216
217 private final Logger log = LoggerFactory.getLogger(OpenFlowSwitchAgent.class);
218 private final Lock switchLock = new ReentrantLock();
219
220 @Override
221 public boolean addConnectedSwitch(Dpid dpid, OpenFlowSwitch sw) {
alshabib9eab22f2014-10-20 17:17:31 -0700222
tom7ef8ff92014-09-17 13:08:06 -0700223 if (connectedSwitches.get(dpid) != null) {
224 log.error("Trying to add connectedSwitch but found a previous "
225 + "value for dpid: {}", dpid);
226 return false;
227 } else {
Yuta HIGUCHIeb3f30b2014-10-22 11:34:49 -0700228 log.info("Added switch {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700229 connectedSwitches.put(dpid, sw);
alshabib8f1cf4a2014-09-17 14:44:48 -0700230 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700231 l.switchAdded(dpid);
232 }
233 return true;
234 }
235 }
236
237 @Override
238 public boolean validActivation(Dpid dpid) {
239 if (connectedSwitches.get(dpid) == null) {
240 log.error("Trying to activate switch but is not in "
241 + "connected switches: dpid {}. Aborting ..",
242 dpid);
243 return false;
244 }
245 if (activeMasterSwitches.get(dpid) != null ||
246 activeEqualSwitches.get(dpid) != null) {
247 log.error("Trying to activate switch but it is already "
248 + "activated: dpid {}. Found in activeMaster: {} "
249 + "Found in activeEqual: {}. Aborting ..", new Object[]{
250 dpid,
251 (activeMasterSwitches.get(dpid) == null) ? 'N' : 'Y',
252 (activeEqualSwitches.get(dpid) == null) ? 'N' : 'Y'});
253 return false;
254 }
255 return true;
256 }
257
258
259 @Override
260 public boolean addActivatedMasterSwitch(Dpid dpid, OpenFlowSwitch sw) {
261 switchLock.lock();
262 try {
263 if (!validActivation(dpid)) {
264 return false;
265 }
266 activeMasterSwitches.put(dpid, sw);
267 return true;
268 } finally {
269 switchLock.unlock();
270 }
271 }
272
273 @Override
274 public boolean addActivatedEqualSwitch(Dpid dpid, OpenFlowSwitch sw) {
275 switchLock.lock();
276 try {
277 if (!validActivation(dpid)) {
278 return false;
279 }
280 activeEqualSwitches.put(dpid, sw);
281 log.info("Added Activated EQUAL Switch {}", dpid);
282 return true;
283 } finally {
284 switchLock.unlock();
285 }
286 }
287
288 @Override
289 public void transitionToMasterSwitch(Dpid dpid) {
290 switchLock.lock();
291 try {
292 if (activeMasterSwitches.containsKey(dpid)) {
293 return;
294 }
295 OpenFlowSwitch sw = activeEqualSwitches.remove(dpid);
296 if (sw == null) {
297 sw = getSwitch(dpid);
298 if (sw == null) {
299 log.error("Transition to master called on sw {}, but switch "
300 + "was not found in controller-cache", dpid);
301 return;
302 }
303 }
304 log.info("Transitioned switch {} to MASTER", dpid);
305 activeMasterSwitches.put(dpid, sw);
306 } finally {
307 switchLock.unlock();
308 }
309 }
310
311
312 @Override
313 public void transitionToEqualSwitch(Dpid dpid) {
314 switchLock.lock();
315 try {
316 if (activeEqualSwitches.containsKey(dpid)) {
317 return;
318 }
319 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
320 if (sw == null) {
321 sw = getSwitch(dpid);
322 if (sw == null) {
323 log.error("Transition to equal called on sw {}, but switch "
324 + "was not found in controller-cache", dpid);
325 return;
326 }
327 }
328 log.info("Transitioned switch {} to EQUAL", dpid);
329 activeEqualSwitches.put(dpid, sw);
330 } finally {
331 switchLock.unlock();
332 }
333
334 }
335
336 @Override
337 public void removeConnectedSwitch(Dpid dpid) {
338 connectedSwitches.remove(dpid);
339 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
340 if (sw == null) {
Ayaka Koshibec4047702014-10-07 14:43:52 -0700341 log.warn("sw was null for {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700342 sw = activeEqualSwitches.remove(dpid);
343 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700344 for (OpenFlowSwitchListener l : ofSwitchListener) {
Ayaka Koshibec4047702014-10-07 14:43:52 -0700345 log.warn("removal for {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700346 l.switchRemoved(dpid);
347 }
348 }
349
350 @Override
351 public void processMessage(Dpid dpid, OFMessage m) {
352 processPacket(dpid, m);
353 }
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700354
355 @Override
356 public void returnRoleAssertFailed(Dpid dpid, RoleState role) {
357 for (OpenFlowSwitchListener l : ofSwitchListener) {
358 l.roleAssertFailed(dpid, role);
359 }
360 }
tom7ef8ff92014-09-17 13:08:06 -0700361 }
362
alshabib8f1cf4a2014-09-17 14:44:48 -0700363 private final class OFMessageHandler implements Runnable {
364
365 private final OFMessage msg;
366 private final Dpid dpid;
367
368 public OFMessageHandler(Dpid dpid, OFMessage msg) {
369 this.msg = msg;
370 this.dpid = dpid;
371 }
372
373 @Override
374 public void run() {
alshabibeec3a062014-09-17 18:01:26 -0700375 for (OpenFlowEventListener listener : ofEventListener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700376 listener.handleMessage(dpid, msg);
377 }
378 }
379
380 }
381
tom7ef8ff92014-09-17 13:08:06 -0700382}