blob: 79d667f319a2317a860ef834b3b44424514c594c [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 */
tom9c94c5b2014-09-17 13:14:42 -070016package org.onlab.onos.openflow.controller.impl;
tom7ef8ff92014-09-17 13:08:06 -070017
alshabib8f1cf4a2014-09-17 14:44:48 -070018import static org.onlab.util.Tools.namedThreads;
19
tom7ef8ff92014-09-17 13:08:06 -070020import java.util.HashSet;
21import java.util.Set;
22import java.util.concurrent.ConcurrentHashMap;
alshabib8f1cf4a2014-09-17 14:44:48 -070023import java.util.concurrent.ExecutorService;
24import java.util.concurrent.Executors;
tom7ef8ff92014-09-17 13:08:06 -070025import java.util.concurrent.locks.Lock;
26import java.util.concurrent.locks.ReentrantLock;
27
28import org.apache.felix.scr.annotations.Activate;
29import org.apache.felix.scr.annotations.Component;
30import org.apache.felix.scr.annotations.Deactivate;
31import org.apache.felix.scr.annotations.Service;
tom9c94c5b2014-09-17 13:14:42 -070032import org.onlab.onos.openflow.controller.DefaultOpenFlowPacketContext;
33import org.onlab.onos.openflow.controller.Dpid;
34import org.onlab.onos.openflow.controller.OpenFlowController;
alshabibeec3a062014-09-17 18:01:26 -070035import org.onlab.onos.openflow.controller.OpenFlowEventListener;
tom9c94c5b2014-09-17 13:14:42 -070036import org.onlab.onos.openflow.controller.OpenFlowPacketContext;
37import org.onlab.onos.openflow.controller.OpenFlowSwitch;
38import org.onlab.onos.openflow.controller.OpenFlowSwitchListener;
39import org.onlab.onos.openflow.controller.PacketListener;
40import org.onlab.onos.openflow.controller.RoleState;
41import org.onlab.onos.openflow.controller.driver.OpenFlowAgent;
tom7ef8ff92014-09-17 13:08:06 -070042import org.projectfloodlight.openflow.protocol.OFMessage;
43import org.projectfloodlight.openflow.protocol.OFPacketIn;
44import org.projectfloodlight.openflow.protocol.OFPortStatus;
Ayaka Koshibe38594c22014-10-22 13:36:12 -070045import org.projectfloodlight.openflow.protocol.OFStatsReply;
46import org.projectfloodlight.openflow.protocol.OFStatsType;
tom7ef8ff92014-09-17 13:08:06 -070047import org.slf4j.Logger;
48import org.slf4j.LoggerFactory;
49
50import com.google.common.collect.ArrayListMultimap;
51import com.google.common.collect.Multimap;
alshabibeec3a062014-09-17 18:01:26 -070052import com.google.common.collect.Sets;
tom7ef8ff92014-09-17 13:08:06 -070053
54@Component(immediate = true)
55@Service
56public class OpenFlowControllerImpl implements OpenFlowController {
57
58 private static final Logger log =
59 LoggerFactory.getLogger(OpenFlowControllerImpl.class);
60
alshabib8f1cf4a2014-09-17 14:44:48 -070061 private final ExecutorService executor = Executors.newFixedThreadPool(16,
alshabibeec3a062014-09-17 18:01:26 -070062 namedThreads("of-event-%d"));
alshabib8f1cf4a2014-09-17 14:44:48 -070063
tom7ef8ff92014-09-17 13:08:06 -070064 protected ConcurrentHashMap<Dpid, OpenFlowSwitch> connectedSwitches =
65 new ConcurrentHashMap<Dpid, OpenFlowSwitch>();
66 protected ConcurrentHashMap<Dpid, OpenFlowSwitch> activeMasterSwitches =
67 new ConcurrentHashMap<Dpid, OpenFlowSwitch>();
68 protected ConcurrentHashMap<Dpid, OpenFlowSwitch> activeEqualSwitches =
69 new ConcurrentHashMap<Dpid, OpenFlowSwitch>();
70
71 protected OpenFlowSwitchAgent agent = new OpenFlowSwitchAgent();
alshabib8f1cf4a2014-09-17 14:44:48 -070072 protected Set<OpenFlowSwitchListener> ofSwitchListener = new HashSet<>();
tom7ef8ff92014-09-17 13:08:06 -070073
74 protected Multimap<Integer, PacketListener> ofPacketListener =
75 ArrayListMultimap.create();
76
alshabibeec3a062014-09-17 18:01:26 -070077 protected Set<OpenFlowEventListener> ofEventListener = Sets.newHashSet();
tom7ef8ff92014-09-17 13:08:06 -070078
79 private final Controller ctrl = new Controller();
80
81 @Activate
82 public void activate() {
83 ctrl.start(agent);
84 }
85
86 @Deactivate
87 public void deactivate() {
88 ctrl.stop();
89 }
90
91 @Override
92 public Iterable<OpenFlowSwitch> getSwitches() {
93 return connectedSwitches.values();
94 }
95
96 @Override
97 public Iterable<OpenFlowSwitch> getMasterSwitches() {
98 return activeMasterSwitches.values();
99 }
100
101 @Override
102 public Iterable<OpenFlowSwitch> getEqualSwitches() {
103 return activeEqualSwitches.values();
104 }
105
106 @Override
107 public OpenFlowSwitch getSwitch(Dpid dpid) {
108 return connectedSwitches.get(dpid);
109 }
110
111 @Override
112 public OpenFlowSwitch getMasterSwitch(Dpid dpid) {
113 return activeMasterSwitches.get(dpid);
114 }
115
116 @Override
117 public OpenFlowSwitch getEqualSwitch(Dpid dpid) {
118 return activeEqualSwitches.get(dpid);
119 }
120
121 @Override
122 public void addListener(OpenFlowSwitchListener listener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700123 if (!ofSwitchListener.contains(listener)) {
124 this.ofSwitchListener.add(listener);
tom7ef8ff92014-09-17 13:08:06 -0700125 }
126 }
127
128 @Override
129 public void removeListener(OpenFlowSwitchListener listener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700130 this.ofSwitchListener.remove(listener);
tom7ef8ff92014-09-17 13:08:06 -0700131 }
132
133 @Override
134 public void addPacketListener(int priority, PacketListener listener) {
135 ofPacketListener.put(priority, listener);
136 }
137
138 @Override
139 public void removePacketListener(PacketListener listener) {
140 ofPacketListener.values().remove(listener);
141 }
142
143 @Override
alshabibeec3a062014-09-17 18:01:26 -0700144 public void addEventListener(OpenFlowEventListener listener) {
145 ofEventListener.add(listener);
146 }
147
148 @Override
149 public void removeEventListener(OpenFlowEventListener listener) {
150 ofEventListener.remove(listener);
151 }
152
153 @Override
tom7ef8ff92014-09-17 13:08:06 -0700154 public void write(Dpid dpid, OFMessage msg) {
155 this.getSwitch(dpid).sendMsg(msg);
156 }
157
158 @Override
159 public void processPacket(Dpid dpid, OFMessage msg) {
160 switch (msg.getType()) {
161 case PORT_STATUS:
alshabib8f1cf4a2014-09-17 14:44:48 -0700162 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700163 l.portChanged(dpid, (OFPortStatus) msg);
164 }
165 break;
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700166 case FEATURES_REPLY:
167 for (OpenFlowSwitchListener l : ofSwitchListener) {
168 l.switchChanged(dpid);
169 }
170 break;
tom7ef8ff92014-09-17 13:08:06 -0700171 case PACKET_IN:
172 OpenFlowPacketContext pktCtx = DefaultOpenFlowPacketContext
173 .packetContextFromPacketIn(this.getSwitch(dpid),
174 (OFPacketIn) msg);
175 for (PacketListener p : ofPacketListener.values()) {
176 p.handlePacket(pktCtx);
177 }
178 break;
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700179 case STATS_REPLY:
180 OFStatsReply reply = (OFStatsReply) msg;
181 if (reply.getStatsType().equals(OFStatsType.PORT_DESC)) {
182 for (OpenFlowSwitchListener l : ofSwitchListener) {
183 l.switchChanged(dpid);
184 }
185 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700186 case FLOW_REMOVED:
187 case ERROR:
alshabib8f1cf4a2014-09-17 14:44:48 -0700188 case BARRIER_REPLY:
189 executor.submit(new OFMessageHandler(dpid, msg));
190 break;
tom7ef8ff92014-09-17 13:08:06 -0700191 default:
192 log.warn("Handling message type {} not yet implemented {}",
193 msg.getType(), msg);
194 }
195 }
196
197 @Override
198 public void setRole(Dpid dpid, RoleState role) {
Yuta HIGUCHI79cbd1c2014-10-02 16:57:57 -0700199 final OpenFlowSwitch sw = getSwitch(dpid);
200 if (sw == null) {
201 log.debug("Switch not connected. Ignoring setRole({}, {})", dpid, role);
202 return;
203 }
204 sw.setRole(role);
tom7ef8ff92014-09-17 13:08:06 -0700205 }
206
207 /**
208 * Implementation of an OpenFlow Agent which is responsible for
209 * keeping track of connected switches and the state in which
210 * they are.
211 */
212 public class OpenFlowSwitchAgent implements OpenFlowAgent {
213
214 private final Logger log = LoggerFactory.getLogger(OpenFlowSwitchAgent.class);
215 private final Lock switchLock = new ReentrantLock();
216
217 @Override
218 public boolean addConnectedSwitch(Dpid dpid, OpenFlowSwitch sw) {
alshabib9eab22f2014-10-20 17:17:31 -0700219
tom7ef8ff92014-09-17 13:08:06 -0700220 if (connectedSwitches.get(dpid) != null) {
221 log.error("Trying to add connectedSwitch but found a previous "
222 + "value for dpid: {}", dpid);
223 return false;
224 } else {
Yuta HIGUCHIeb3f30b2014-10-22 11:34:49 -0700225 log.info("Added switch {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700226 connectedSwitches.put(dpid, sw);
alshabib8f1cf4a2014-09-17 14:44:48 -0700227 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700228 l.switchAdded(dpid);
229 }
230 return true;
231 }
232 }
233
234 @Override
235 public boolean validActivation(Dpid dpid) {
236 if (connectedSwitches.get(dpid) == null) {
237 log.error("Trying to activate switch but is not in "
238 + "connected switches: dpid {}. Aborting ..",
239 dpid);
240 return false;
241 }
242 if (activeMasterSwitches.get(dpid) != null ||
243 activeEqualSwitches.get(dpid) != null) {
244 log.error("Trying to activate switch but it is already "
245 + "activated: dpid {}. Found in activeMaster: {} "
246 + "Found in activeEqual: {}. Aborting ..", new Object[]{
247 dpid,
248 (activeMasterSwitches.get(dpid) == null) ? 'N' : 'Y',
249 (activeEqualSwitches.get(dpid) == null) ? 'N' : 'Y'});
250 return false;
251 }
252 return true;
253 }
254
255
256 @Override
257 public boolean addActivatedMasterSwitch(Dpid dpid, OpenFlowSwitch sw) {
258 switchLock.lock();
259 try {
260 if (!validActivation(dpid)) {
261 return false;
262 }
263 activeMasterSwitches.put(dpid, sw);
264 return true;
265 } finally {
266 switchLock.unlock();
267 }
268 }
269
270 @Override
271 public boolean addActivatedEqualSwitch(Dpid dpid, OpenFlowSwitch sw) {
272 switchLock.lock();
273 try {
274 if (!validActivation(dpid)) {
275 return false;
276 }
277 activeEqualSwitches.put(dpid, sw);
278 log.info("Added Activated EQUAL Switch {}", dpid);
279 return true;
280 } finally {
281 switchLock.unlock();
282 }
283 }
284
285 @Override
286 public void transitionToMasterSwitch(Dpid dpid) {
287 switchLock.lock();
288 try {
289 if (activeMasterSwitches.containsKey(dpid)) {
290 return;
291 }
292 OpenFlowSwitch sw = activeEqualSwitches.remove(dpid);
293 if (sw == null) {
294 sw = getSwitch(dpid);
295 if (sw == null) {
296 log.error("Transition to master called on sw {}, but switch "
297 + "was not found in controller-cache", dpid);
298 return;
299 }
300 }
301 log.info("Transitioned switch {} to MASTER", dpid);
302 activeMasterSwitches.put(dpid, sw);
303 } finally {
304 switchLock.unlock();
305 }
306 }
307
308
309 @Override
310 public void transitionToEqualSwitch(Dpid dpid) {
311 switchLock.lock();
312 try {
313 if (activeEqualSwitches.containsKey(dpid)) {
314 return;
315 }
316 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
317 if (sw == null) {
318 sw = getSwitch(dpid);
319 if (sw == null) {
320 log.error("Transition to equal called on sw {}, but switch "
321 + "was not found in controller-cache", dpid);
322 return;
323 }
324 }
325 log.info("Transitioned switch {} to EQUAL", dpid);
326 activeEqualSwitches.put(dpid, sw);
327 } finally {
328 switchLock.unlock();
329 }
330
331 }
332
333 @Override
334 public void removeConnectedSwitch(Dpid dpid) {
335 connectedSwitches.remove(dpid);
336 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
337 if (sw == null) {
Ayaka Koshibec4047702014-10-07 14:43:52 -0700338 log.warn("sw was null for {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700339 sw = activeEqualSwitches.remove(dpid);
340 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700341 for (OpenFlowSwitchListener l : ofSwitchListener) {
Ayaka Koshibec4047702014-10-07 14:43:52 -0700342 log.warn("removal for {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700343 l.switchRemoved(dpid);
344 }
345 }
346
347 @Override
348 public void processMessage(Dpid dpid, OFMessage m) {
349 processPacket(dpid, m);
350 }
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700351
352 @Override
353 public void returnRoleAssertFailed(Dpid dpid, RoleState role) {
354 for (OpenFlowSwitchListener l : ofSwitchListener) {
355 l.roleAssertFailed(dpid, role);
356 }
357 }
tom7ef8ff92014-09-17 13:08:06 -0700358 }
359
alshabib8f1cf4a2014-09-17 14:44:48 -0700360 private final class OFMessageHandler implements Runnable {
361
362 private final OFMessage msg;
363 private final Dpid dpid;
364
365 public OFMessageHandler(Dpid dpid, OFMessage msg) {
366 this.msg = msg;
367 this.dpid = dpid;
368 }
369
370 @Override
371 public void run() {
alshabibeec3a062014-09-17 18:01:26 -0700372 for (OpenFlowEventListener listener : ofEventListener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700373 listener.handleMessage(dpid, msg);
374 }
375 }
376
377 }
378
tom7ef8ff92014-09-17 13:08:06 -0700379}