blob: 098771d9911d87049851dad6880f147253653d3b [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;
Marc De Leenheer631ffce2014-10-28 16:29:07 -070042import org.projectfloodlight.openflow.protocol.OFCircuitPortStatus;
43import org.projectfloodlight.openflow.protocol.OFExperimenter;
tom7ef8ff92014-09-17 13:08:06 -070044import org.projectfloodlight.openflow.protocol.OFMessage;
45import org.projectfloodlight.openflow.protocol.OFPacketIn;
Marc De Leenheer631ffce2014-10-28 16:29:07 -070046import org.projectfloodlight.openflow.protocol.OFPortDesc;
tom7ef8ff92014-09-17 13:08:06 -070047import 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 }
Yuta HIGUCHIfca9f492014-10-28 21:39:49 -0700189 // fall through to invoke handler
alshabib8f1cf4a2014-09-17 14:44:48 -0700190 case FLOW_REMOVED:
191 case ERROR:
alshabib8f1cf4a2014-09-17 14:44:48 -0700192 case BARRIER_REPLY:
193 executor.submit(new OFMessageHandler(dpid, msg));
194 break;
Marc De Leenheer631ffce2014-10-28 16:29:07 -0700195 case EXPERIMENTER:
196 // Handle optical port stats
197 if (((OFExperimenter) msg).getExperimenter() == 0x748771) {
198 OFCircuitPortStatus circuitPortStatus = (OFCircuitPortStatus) msg;
199 OFPortStatus.Builder portStatus = this.getSwitch(dpid).factory().buildPortStatus();
200 OFPortDesc.Builder portDesc = this.getSwitch(dpid).factory().buildPortDesc();
201 portDesc.setPortNo(circuitPortStatus.getPortNo())
202 .setHwAddr(circuitPortStatus.getHwAddr())
203 .setName(circuitPortStatus.getName())
204 .setConfig(circuitPortStatus.getConfig())
205 .setState(circuitPortStatus.getState());
206 portStatus.setReason(circuitPortStatus.getReason()).setDesc(portDesc.build());
207 for (OpenFlowSwitchListener l : ofSwitchListener) {
208 l.portChanged(dpid, portStatus.build());
209 }
210 } else {
211 log.warn("Handling experimenter type {} not yet implemented",
212 ((OFExperimenter) msg).getExperimenter(), msg);
213 }
214 break;
tom7ef8ff92014-09-17 13:08:06 -0700215 default:
216 log.warn("Handling message type {} not yet implemented {}",
217 msg.getType(), msg);
218 }
219 }
220
221 @Override
222 public void setRole(Dpid dpid, RoleState role) {
Yuta HIGUCHI79cbd1c2014-10-02 16:57:57 -0700223 final OpenFlowSwitch sw = getSwitch(dpid);
224 if (sw == null) {
225 log.debug("Switch not connected. Ignoring setRole({}, {})", dpid, role);
226 return;
227 }
228 sw.setRole(role);
tom7ef8ff92014-09-17 13:08:06 -0700229 }
230
231 /**
232 * Implementation of an OpenFlow Agent which is responsible for
233 * keeping track of connected switches and the state in which
234 * they are.
235 */
236 public class OpenFlowSwitchAgent implements OpenFlowAgent {
237
238 private final Logger log = LoggerFactory.getLogger(OpenFlowSwitchAgent.class);
239 private final Lock switchLock = new ReentrantLock();
240
241 @Override
242 public boolean addConnectedSwitch(Dpid dpid, OpenFlowSwitch sw) {
alshabib9eab22f2014-10-20 17:17:31 -0700243
tom7ef8ff92014-09-17 13:08:06 -0700244 if (connectedSwitches.get(dpid) != null) {
245 log.error("Trying to add connectedSwitch but found a previous "
246 + "value for dpid: {}", dpid);
247 return false;
248 } else {
Yuta HIGUCHIeb3f30b2014-10-22 11:34:49 -0700249 log.info("Added switch {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700250 connectedSwitches.put(dpid, sw);
alshabib8f1cf4a2014-09-17 14:44:48 -0700251 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700252 l.switchAdded(dpid);
253 }
254 return true;
255 }
256 }
257
258 @Override
259 public boolean validActivation(Dpid dpid) {
260 if (connectedSwitches.get(dpid) == null) {
261 log.error("Trying to activate switch but is not in "
262 + "connected switches: dpid {}. Aborting ..",
263 dpid);
264 return false;
265 }
266 if (activeMasterSwitches.get(dpid) != null ||
267 activeEqualSwitches.get(dpid) != null) {
268 log.error("Trying to activate switch but it is already "
269 + "activated: dpid {}. Found in activeMaster: {} "
270 + "Found in activeEqual: {}. Aborting ..", new Object[]{
271 dpid,
272 (activeMasterSwitches.get(dpid) == null) ? 'N' : 'Y',
273 (activeEqualSwitches.get(dpid) == null) ? 'N' : 'Y'});
274 return false;
275 }
276 return true;
277 }
278
279
280 @Override
281 public boolean addActivatedMasterSwitch(Dpid dpid, OpenFlowSwitch sw) {
282 switchLock.lock();
283 try {
284 if (!validActivation(dpid)) {
285 return false;
286 }
287 activeMasterSwitches.put(dpid, sw);
288 return true;
289 } finally {
290 switchLock.unlock();
291 }
292 }
293
294 @Override
295 public boolean addActivatedEqualSwitch(Dpid dpid, OpenFlowSwitch sw) {
296 switchLock.lock();
297 try {
298 if (!validActivation(dpid)) {
299 return false;
300 }
301 activeEqualSwitches.put(dpid, sw);
302 log.info("Added Activated EQUAL Switch {}", dpid);
303 return true;
304 } finally {
305 switchLock.unlock();
306 }
307 }
308
309 @Override
310 public void transitionToMasterSwitch(Dpid dpid) {
311 switchLock.lock();
312 try {
313 if (activeMasterSwitches.containsKey(dpid)) {
314 return;
315 }
316 OpenFlowSwitch sw = activeEqualSwitches.remove(dpid);
317 if (sw == null) {
318 sw = getSwitch(dpid);
319 if (sw == null) {
320 log.error("Transition to master called on sw {}, but switch "
321 + "was not found in controller-cache", dpid);
322 return;
323 }
324 }
325 log.info("Transitioned switch {} to MASTER", dpid);
326 activeMasterSwitches.put(dpid, sw);
327 } finally {
328 switchLock.unlock();
329 }
330 }
331
332
333 @Override
334 public void transitionToEqualSwitch(Dpid dpid) {
335 switchLock.lock();
336 try {
337 if (activeEqualSwitches.containsKey(dpid)) {
338 return;
339 }
340 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
341 if (sw == null) {
342 sw = getSwitch(dpid);
343 if (sw == null) {
344 log.error("Transition to equal called on sw {}, but switch "
345 + "was not found in controller-cache", dpid);
346 return;
347 }
348 }
349 log.info("Transitioned switch {} to EQUAL", dpid);
350 activeEqualSwitches.put(dpid, sw);
351 } finally {
352 switchLock.unlock();
353 }
354
355 }
356
357 @Override
358 public void removeConnectedSwitch(Dpid dpid) {
359 connectedSwitches.remove(dpid);
360 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
361 if (sw == null) {
Ayaka Koshibec4047702014-10-07 14:43:52 -0700362 log.warn("sw was null for {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700363 sw = activeEqualSwitches.remove(dpid);
364 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700365 for (OpenFlowSwitchListener l : ofSwitchListener) {
Ayaka Koshibec4047702014-10-07 14:43:52 -0700366 log.warn("removal for {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700367 l.switchRemoved(dpid);
368 }
369 }
370
371 @Override
372 public void processMessage(Dpid dpid, OFMessage m) {
373 processPacket(dpid, m);
374 }
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700375
376 @Override
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700377 public void returnRoleReply(Dpid dpid, RoleState requested, RoleState response) {
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700378 for (OpenFlowSwitchListener l : ofSwitchListener) {
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700379 l.receivedRoleReply(dpid, requested, response);
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700380 }
381 }
tom7ef8ff92014-09-17 13:08:06 -0700382 }
383
alshabib8f1cf4a2014-09-17 14:44:48 -0700384 private final class OFMessageHandler implements Runnable {
385
386 private final OFMessage msg;
387 private final Dpid dpid;
388
389 public OFMessageHandler(Dpid dpid, OFMessage msg) {
390 this.msg = msg;
391 this.dpid = dpid;
392 }
393
394 @Override
395 public void run() {
alshabibeec3a062014-09-17 18:01:26 -0700396 for (OpenFlowEventListener listener : ofEventListener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700397 listener.handleMessage(dpid, msg);
398 }
399 }
400
401 }
402
tom7ef8ff92014-09-17 13:08:06 -0700403}