blob: cde105fd2da22be02afaaafcb61e45234699965e [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present 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
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080018import com.google.common.collect.ArrayListMultimap;
alshabib64def642014-12-02 23:27:37 -080019import com.google.common.collect.Lists;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080020import com.google.common.collect.Multimap;
tom7ef8ff92014-09-17 13:08:06 -070021import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Deactivate;
Jonathan Hartbbd91d42015-02-27 11:18:04 -080024import org.apache.felix.scr.annotations.Modified;
Charles Chan3b00e1b2015-08-26 23:12:52 +080025import org.apache.felix.scr.annotations.Property;
alshabibb452fd72015-04-22 20:46:20 -070026import org.apache.felix.scr.annotations.Reference;
27import org.apache.felix.scr.annotations.ReferenceCardinality;
tom7ef8ff92014-09-17 13:08:06 -070028import org.apache.felix.scr.annotations.Service;
Charles Chan3b00e1b2015-08-26 23:12:52 +080029import org.onosproject.cfg.ComponentConfigService;
Charles Chanecfdfb72015-11-24 19:05:50 -080030import org.onosproject.core.CoreService;
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -070031import org.onosproject.net.device.DeviceEvent;
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -070032import org.onosproject.net.device.DeviceListener;
33import org.onosproject.net.device.DeviceService;
Thomas Vachuska3358af22015-05-19 18:40:34 -070034import org.onosproject.net.driver.DefaultDriverProviderService;
alshabibb452fd72015-04-22 20:46:20 -070035import org.onosproject.net.driver.DriverService;
Brian O'Connorabafb502014-12-02 22:26:20 -080036import org.onosproject.openflow.controller.DefaultOpenFlowPacketContext;
37import org.onosproject.openflow.controller.Dpid;
38import org.onosproject.openflow.controller.OpenFlowController;
39import org.onosproject.openflow.controller.OpenFlowEventListener;
Jian Lia78cdb22016-04-21 13:03:58 -070040import org.onosproject.openflow.controller.OpenFlowMessageListener;
Brian O'Connorabafb502014-12-02 22:26:20 -080041import org.onosproject.openflow.controller.OpenFlowPacketContext;
42import org.onosproject.openflow.controller.OpenFlowSwitch;
43import org.onosproject.openflow.controller.OpenFlowSwitchListener;
44import org.onosproject.openflow.controller.PacketListener;
45import org.onosproject.openflow.controller.RoleState;
46import org.onosproject.openflow.controller.driver.OpenFlowAgent;
Jonathan Hartbbd91d42015-02-27 11:18:04 -080047import org.osgi.service.component.ComponentContext;
Marc De Leenheerb9311372015-07-09 11:36:49 -070048import org.projectfloodlight.openflow.protocol.OFCalientFlowStatsEntry;
49import org.projectfloodlight.openflow.protocol.OFCalientFlowStatsReply;
Marc De Leenheer631ffce2014-10-28 16:29:07 -070050import org.projectfloodlight.openflow.protocol.OFCircuitPortStatus;
51import org.projectfloodlight.openflow.protocol.OFExperimenter;
alshabib64def642014-12-02 23:27:37 -080052import org.projectfloodlight.openflow.protocol.OFFactories;
53import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry;
54import org.projectfloodlight.openflow.protocol.OFFlowStatsReply;
sangho6a0bb172015-02-05 12:24:48 -080055import org.projectfloodlight.openflow.protocol.OFGroupDescStatsEntry;
56import org.projectfloodlight.openflow.protocol.OFGroupDescStatsReply;
57import org.projectfloodlight.openflow.protocol.OFGroupStatsEntry;
58import org.projectfloodlight.openflow.protocol.OFGroupStatsReply;
tom7ef8ff92014-09-17 13:08:06 -070059import org.projectfloodlight.openflow.protocol.OFMessage;
60import org.projectfloodlight.openflow.protocol.OFPacketIn;
Marc De Leenheer631ffce2014-10-28 16:29:07 -070061import org.projectfloodlight.openflow.protocol.OFPortDesc;
sangho538108b2015-04-08 14:29:20 -070062import org.projectfloodlight.openflow.protocol.OFPortStatsEntry;
63import org.projectfloodlight.openflow.protocol.OFPortStatsReply;
tom7ef8ff92014-09-17 13:08:06 -070064import org.projectfloodlight.openflow.protocol.OFPortStatus;
Ayaka Koshibe38594c22014-10-22 13:36:12 -070065import org.projectfloodlight.openflow.protocol.OFStatsReply;
alshabib64def642014-12-02 23:27:37 -080066import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
Jian Li2266bff2016-04-21 11:01:25 -070067import org.projectfloodlight.openflow.protocol.OFTableStatsEntry;
68import org.projectfloodlight.openflow.protocol.OFTableStatsReply;
Marc De Leenheerb9311372015-07-09 11:36:49 -070069import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
70import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
tom7ef8ff92014-09-17 13:08:06 -070071import org.slf4j.Logger;
72import org.slf4j.LoggerFactory;
73
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080074import java.util.Collection;
Marc De Leenheerb9311372015-07-09 11:36:49 -070075import java.util.Collections;
Marc De Leenheerb9311372015-07-09 11:36:49 -070076import java.util.LinkedList;
77import java.util.List;
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -070078import java.util.Optional;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080079import java.util.Set;
Marc De Leenheer8aba62f2017-04-25 14:33:37 -070080import java.util.concurrent.CompletableFuture;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080081import java.util.concurrent.ConcurrentHashMap;
HIGUCHI Yuta1979f552015-12-28 21:24:26 -080082import java.util.concurrent.ConcurrentMap;
Jonathan Hart6d44d192015-05-11 18:01:19 -070083import java.util.concurrent.CopyOnWriteArraySet;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080084import java.util.concurrent.ExecutorService;
85import java.util.concurrent.Executors;
86import java.util.concurrent.locks.Lock;
87import java.util.concurrent.locks.ReentrantLock;
Jian Li2266bff2016-04-21 11:01:25 -070088
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080089import static org.onlab.util.Tools.groupedThreads;
Hyunsun Moone71494d2016-11-15 14:45:25 -080090import static org.onosproject.net.Device.Type.CONTROLLER;
91import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_REMOVED;
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -070092import static org.onosproject.openflow.controller.Dpid.dpid;
tom7ef8ff92014-09-17 13:08:06 -070093
94@Component(immediate = true)
95@Service
96public class OpenFlowControllerImpl implements OpenFlowController {
Charles Chanecfdfb72015-11-24 19:05:50 -080097 private static final String APP_ID = "org.onosproject.openflow-base";
Brian O'Connorff278502015-09-22 14:49:52 -070098 private static final String DEFAULT_OFPORT = "6633,6653";
Yuta HIGUCHI8552b172016-07-25 12:10:08 -070099 private static final int DEFAULT_WORKER_THREADS = 0;
tom7ef8ff92014-09-17 13:08:06 -0700100
101 private static final Logger log =
102 LoggerFactory.getLogger(OpenFlowControllerImpl.class);
103
alshabibb452fd72015-04-22 20:46:20 -0700104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Charles Chanecfdfb72015-11-24 19:05:50 -0800105 protected CoreService coreService;
106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabibb452fd72015-04-22 20:46:20 -0700108 protected DriverService driverService;
109
Thomas Vachuska3358af22015-05-19 18:40:34 -0700110 // References exists merely for sequencing purpose to assure drivers are loaded
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 protected DefaultDriverProviderService defaultDriverProviderService;
113
Charles Chan3b00e1b2015-08-26 23:12:52 +0800114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected ComponentConfigService cfgService;
116
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
118 protected DeviceService deviceService;
119
120
Brian O'Connore2a399e2015-09-22 15:32:50 -0700121 @Property(name = "openflowPorts", value = DEFAULT_OFPORT,
122 label = "Port numbers (comma separated) used by OpenFlow protocol; default is 6633,6653")
123 private String openflowPorts = DEFAULT_OFPORT;
Charles Chan3b00e1b2015-08-26 23:12:52 +0800124
125 @Property(name = "workerThreads", intValue = DEFAULT_WORKER_THREADS,
Yuta HIGUCHI8552b172016-07-25 12:10:08 -0700126 label = "Number of controller worker threads")
Charles Chan3b00e1b2015-08-26 23:12:52 +0800127 private int workerThreads = DEFAULT_WORKER_THREADS;
128
Ray Milkey7ec0d1b2015-11-13 08:51:35 -0800129 protected ExecutorService executorMsgs =
Andrea Campanelladda93562016-03-02 11:08:12 -0800130 Executors.newFixedThreadPool(32, groupedThreads("onos/of", "event-stats-%d", log));
Pavlin Radoslavov369c6432014-12-03 16:25:14 -0800131
132 private final ExecutorService executorBarrier =
Andrea Campanelladda93562016-03-02 11:08:12 -0800133 Executors.newFixedThreadPool(4, groupedThreads("onos/of", "event-barrier-%d", log));
alshabib8f1cf4a2014-09-17 14:44:48 -0700134
Prince Pereirae7798032016-07-08 16:31:58 +0530135 //Separate executor thread for handling error messages and barrier replies for same failed
136 // transactions to avoid context switching of thread
137 protected ExecutorService executorErrorMsgs =
138 Executors.newSingleThreadExecutor(groupedThreads("onos/of", "event-error-msg-%d", log));
139
140 //concurrent hashmap to track failed transactions
141 protected ConcurrentMap<Long, Boolean> errorMsgs =
142 new ConcurrentHashMap<>();
HIGUCHI Yuta1979f552015-12-28 21:24:26 -0800143 protected ConcurrentMap<Dpid, OpenFlowSwitch> connectedSwitches =
Jonathan Hart6d44d192015-05-11 18:01:19 -0700144 new ConcurrentHashMap<>();
HIGUCHI Yuta1979f552015-12-28 21:24:26 -0800145 protected ConcurrentMap<Dpid, OpenFlowSwitch> activeMasterSwitches =
Jonathan Hart6d44d192015-05-11 18:01:19 -0700146 new ConcurrentHashMap<>();
HIGUCHI Yuta1979f552015-12-28 21:24:26 -0800147 protected ConcurrentMap<Dpid, OpenFlowSwitch> activeEqualSwitches =
Jonathan Hart6d44d192015-05-11 18:01:19 -0700148 new ConcurrentHashMap<>();
tom7ef8ff92014-09-17 13:08:06 -0700149
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700150 // Key: dpid, value: map with key: long (XID), value: completable future
151 protected ConcurrentMap<Dpid, ConcurrentMap<Long, CompletableFuture<OFMessage>>> responses =
152 new ConcurrentHashMap<>();
153
tom7ef8ff92014-09-17 13:08:06 -0700154 protected OpenFlowSwitchAgent agent = new OpenFlowSwitchAgent();
Jonathan Hart6d44d192015-05-11 18:01:19 -0700155 protected Set<OpenFlowSwitchListener> ofSwitchListener = new CopyOnWriteArraySet<>();
tom7ef8ff92014-09-17 13:08:06 -0700156
157 protected Multimap<Integer, PacketListener> ofPacketListener =
158 ArrayListMultimap.create();
159
Jonathan Hart6d44d192015-05-11 18:01:19 -0700160 protected Set<OpenFlowEventListener> ofEventListener = new CopyOnWriteArraySet<>();
tom7ef8ff92014-09-17 13:08:06 -0700161
Jian Lia78cdb22016-04-21 13:03:58 -0700162 protected Set<OpenFlowMessageListener> ofMessageListener = new CopyOnWriteArraySet<>();
Jian Li28247b52016-01-07 17:24:15 -0800163
sangho6a0bb172015-02-05 12:24:48 -0800164 protected Multimap<Dpid, OFFlowStatsEntry> fullFlowStats =
165 ArrayListMultimap.create();
166
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700167 protected Multimap<Dpid, OFTableStatsEntry> fullTableStats =
168 ArrayListMultimap.create();
169
sangho6a0bb172015-02-05 12:24:48 -0800170 protected Multimap<Dpid, OFGroupStatsEntry> fullGroupStats =
171 ArrayListMultimap.create();
172
173 protected Multimap<Dpid, OFGroupDescStatsEntry> fullGroupDescStats =
alshabib64def642014-12-02 23:27:37 -0800174 ArrayListMultimap.create();
175
sangho538108b2015-04-08 14:29:20 -0700176 protected Multimap<Dpid, OFPortStatsEntry> fullPortStats =
177 ArrayListMultimap.create();
178
tom7ef8ff92014-09-17 13:08:06 -0700179 private final Controller ctrl = new Controller();
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700180 private InternalDeviceListener listener = new InternalDeviceListener();
tom7ef8ff92014-09-17 13:08:06 -0700181
182 @Activate
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800183 public void activate(ComponentContext context) {
Andrea Campanella3556f362016-04-28 15:18:10 -0700184 coreService.registerApplication(APP_ID, this::cleanup);
Charles Chan3b00e1b2015-08-26 23:12:52 +0800185 cfgService.registerProperties(getClass());
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700186 deviceService.addListener(listener);
Brian O'Connorff278502015-09-22 14:49:52 -0700187 ctrl.setConfigParams(context.getProperties());
alshabibb452fd72015-04-22 20:46:20 -0700188 ctrl.start(agent, driverService);
tom7ef8ff92014-09-17 13:08:06 -0700189 }
190
Andrea Campanella3556f362016-04-28 15:18:10 -0700191 private void cleanup() {
192 // Close listening channel and all OF channels. Clean information about switches
193 // before deactivating
Charles Chanecfdfb72015-11-24 19:05:50 -0800194 ctrl.stop();
195 connectedSwitches.values().forEach(OpenFlowSwitch::disconnectSwitch);
Andrea Campanella3556f362016-04-28 15:18:10 -0700196 connectedSwitches.clear();
197 activeMasterSwitches.clear();
198 activeEqualSwitches.clear();
Charles Chanecfdfb72015-11-24 19:05:50 -0800199 }
200
tom7ef8ff92014-09-17 13:08:06 -0700201 @Deactivate
202 public void deactivate() {
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700203 deviceService.removeListener(listener);
Thiago Santos61725402016-08-05 17:58:56 -0300204 cleanup();
Charles Chan3b00e1b2015-08-26 23:12:52 +0800205 cfgService.unregisterProperties(getClass(), false);
tom7ef8ff92014-09-17 13:08:06 -0700206 }
207
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800208 @Modified
209 public void modified(ComponentContext context) {
Charles Chan3b00e1b2015-08-26 23:12:52 +0800210 ctrl.stop();
Brian O'Connorff278502015-09-22 14:49:52 -0700211 ctrl.setConfigParams(context.getProperties());
Charles Chan3b00e1b2015-08-26 23:12:52 +0800212 ctrl.start(agent, driverService);
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800213 }
214
tom7ef8ff92014-09-17 13:08:06 -0700215 @Override
216 public Iterable<OpenFlowSwitch> getSwitches() {
217 return connectedSwitches.values();
218 }
219
220 @Override
221 public Iterable<OpenFlowSwitch> getMasterSwitches() {
222 return activeMasterSwitches.values();
223 }
224
225 @Override
226 public Iterable<OpenFlowSwitch> getEqualSwitches() {
227 return activeEqualSwitches.values();
228 }
229
230 @Override
231 public OpenFlowSwitch getSwitch(Dpid dpid) {
232 return connectedSwitches.get(dpid);
233 }
234
235 @Override
236 public OpenFlowSwitch getMasterSwitch(Dpid dpid) {
237 return activeMasterSwitches.get(dpid);
238 }
239
240 @Override
241 public OpenFlowSwitch getEqualSwitch(Dpid dpid) {
242 return activeEqualSwitches.get(dpid);
243 }
244
245 @Override
246 public void addListener(OpenFlowSwitchListener listener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700247 if (!ofSwitchListener.contains(listener)) {
248 this.ofSwitchListener.add(listener);
tom7ef8ff92014-09-17 13:08:06 -0700249 }
250 }
251
252 @Override
253 public void removeListener(OpenFlowSwitchListener listener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700254 this.ofSwitchListener.remove(listener);
tom7ef8ff92014-09-17 13:08:06 -0700255 }
256
257 @Override
Jian Lia78cdb22016-04-21 13:03:58 -0700258 public void addMessageListener(OpenFlowMessageListener listener) {
259 ofMessageListener.add(listener);
260 }
261
262 @Override
263 public void removeMessageListener(OpenFlowMessageListener listener) {
264 ofMessageListener.remove(listener);
265 }
266
267 @Override
tom7ef8ff92014-09-17 13:08:06 -0700268 public void addPacketListener(int priority, PacketListener listener) {
269 ofPacketListener.put(priority, listener);
270 }
271
272 @Override
273 public void removePacketListener(PacketListener listener) {
274 ofPacketListener.values().remove(listener);
275 }
276
277 @Override
alshabibeec3a062014-09-17 18:01:26 -0700278 public void addEventListener(OpenFlowEventListener listener) {
279 ofEventListener.add(listener);
280 }
281
282 @Override
283 public void removeEventListener(OpenFlowEventListener listener) {
284 ofEventListener.remove(listener);
285 }
286
287 @Override
tom7ef8ff92014-09-17 13:08:06 -0700288 public void write(Dpid dpid, OFMessage msg) {
289 this.getSwitch(dpid).sendMsg(msg);
290 }
291
292 @Override
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700293 public CompletableFuture<OFMessage> writeResponse(Dpid dpid, OFMessage msg) {
294 write(dpid, msg);
295
296 ConcurrentMap<Long, CompletableFuture<OFMessage>> xids =
297 responses.computeIfAbsent(dpid, k -> new ConcurrentHashMap());
298
299 CompletableFuture<OFMessage> future = new CompletableFuture();
300 xids.put(msg.getXid(), future);
301
302 return future;
303 }
304
305 @Override
tom7ef8ff92014-09-17 13:08:06 -0700306 public void processPacket(Dpid dpid, OFMessage msg) {
sangho6a0bb172015-02-05 12:24:48 -0800307 Collection<OFFlowStatsEntry> flowStats;
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700308 Collection<OFTableStatsEntry> tableStats;
sangho6a0bb172015-02-05 12:24:48 -0800309 Collection<OFGroupStatsEntry> groupStats;
310 Collection<OFGroupDescStatsEntry> groupDescStats;
sangho538108b2015-04-08 14:29:20 -0700311 Collection<OFPortStatsEntry> portStats;
sangho6a0bb172015-02-05 12:24:48 -0800312
sangyun-han69ed4462016-07-27 12:10:12 +0900313 OpenFlowSwitch sw = this.getSwitch(dpid);
314
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700315 // Check if someone is waiting for this message
316 ConcurrentMap<Long, CompletableFuture<OFMessage>> xids = responses.get(dpid);
317 if (xids != null && xids.containsKey(msg.getXid())) {
318 xids.remove(msg.getXid()).complete(msg);
319 }
320
tom7ef8ff92014-09-17 13:08:06 -0700321 switch (msg.getType()) {
322 case PORT_STATUS:
alshabib8f1cf4a2014-09-17 14:44:48 -0700323 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700324 l.portChanged(dpid, (OFPortStatus) msg);
325 }
326 break;
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700327 case FEATURES_REPLY:
328 for (OpenFlowSwitchListener l : ofSwitchListener) {
329 l.switchChanged(dpid);
330 }
331 break;
tom7ef8ff92014-09-17 13:08:06 -0700332 case PACKET_IN:
sangyun-han69ed4462016-07-27 12:10:12 +0900333 if (sw == null) {
334 log.error("Switch {} is not found", dpid);
335 break;
336 }
tom7ef8ff92014-09-17 13:08:06 -0700337 OpenFlowPacketContext pktCtx = DefaultOpenFlowPacketContext
sangyun-han69ed4462016-07-27 12:10:12 +0900338 .packetContextFromPacketIn(sw, (OFPacketIn) msg);
tom7ef8ff92014-09-17 13:08:06 -0700339 for (PacketListener p : ofPacketListener.values()) {
340 p.handlePacket(pktCtx);
341 }
342 break;
Pavlin Radoslavov369c6432014-12-03 16:25:14 -0800343 // TODO: Consider using separate threadpool for sensitive messages.
344 // ie. Back to back error could cause us to starve.
345 case FLOW_REMOVED:
Andrea Campanelladda93562016-03-02 11:08:12 -0800346 executorMsgs.execute(new OFMessageHandler(dpid, msg));
Pavlin Radoslavov369c6432014-12-03 16:25:14 -0800347 break;
Prince Pereirae7798032016-07-08 16:31:58 +0530348 case ERROR:
349 log.debug("Received error message from {}: {}", dpid, msg);
350 errorMsgs.putIfAbsent(msg.getXid(), true);
351 executorErrorMsgs.execute(new OFMessageHandler(dpid, msg));
352 break;
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700353 case STATS_REPLY:
354 OFStatsReply reply = (OFStatsReply) msg;
sangho6a0bb172015-02-05 12:24:48 -0800355 switch (reply.getStatsType()) {
356 case PORT_DESC:
357 for (OpenFlowSwitchListener l : ofSwitchListener) {
358 l.switchChanged(dpid);
359 }
360 break;
361 case FLOW:
362 flowStats = publishFlowStats(dpid, (OFFlowStatsReply) reply);
363 if (flowStats != null) {
364 OFFlowStatsReply.Builder rep =
365 OFFactories.getFactory(msg.getVersion()).buildFlowStatsReply();
366 rep.setEntries(Lists.newLinkedList(flowStats));
ssyoon9030fbcd92015-08-17 10:42:07 +0900367 rep.setXid(reply.getXid());
Andrea Campanelladda93562016-03-02 11:08:12 -0800368 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
sangho6a0bb172015-02-05 12:24:48 -0800369 }
370 break;
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700371 case TABLE:
372 tableStats = publishTableStats(dpid, (OFTableStatsReply) reply);
373 if (tableStats != null) {
374 OFTableStatsReply.Builder rep =
375 OFFactories.getFactory(msg.getVersion()).buildTableStatsReply();
376 rep.setEntries(Lists.newLinkedList(tableStats));
Andrea Campanelladda93562016-03-02 11:08:12 -0800377 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700378 }
379 break;
sangho6a0bb172015-02-05 12:24:48 -0800380 case GROUP:
381 groupStats = publishGroupStats(dpid, (OFGroupStatsReply) reply);
382 if (groupStats != null) {
383 OFGroupStatsReply.Builder rep =
384 OFFactories.getFactory(msg.getVersion()).buildGroupStatsReply();
385 rep.setEntries(Lists.newLinkedList(groupStats));
386 rep.setXid(reply.getXid());
Andrea Campanelladda93562016-03-02 11:08:12 -0800387 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
sangho6a0bb172015-02-05 12:24:48 -0800388 }
389 break;
390 case GROUP_DESC:
391 groupDescStats = publishGroupDescStats(dpid,
392 (OFGroupDescStatsReply) reply);
393 if (groupDescStats != null) {
394 OFGroupDescStatsReply.Builder rep =
Marc De Leenheerb9311372015-07-09 11:36:49 -0700395 OFFactories.getFactory(msg.getVersion()).buildGroupDescStatsReply();
sangho6a0bb172015-02-05 12:24:48 -0800396 rep.setEntries(Lists.newLinkedList(groupDescStats));
397 rep.setXid(reply.getXid());
Andrea Campanelladda93562016-03-02 11:08:12 -0800398 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
sangho6a0bb172015-02-05 12:24:48 -0800399 }
400 break;
sangho538108b2015-04-08 14:29:20 -0700401 case PORT:
Andrea Campanelladda93562016-03-02 11:08:12 -0800402 executorMsgs.execute(new OFMessageHandler(dpid, reply));
sangho538108b2015-04-08 14:29:20 -0700403 break;
alshabib5eb79392015-08-19 18:09:55 -0700404 case METER:
Andrea Campanelladda93562016-03-02 11:08:12 -0800405 executorMsgs.execute(new OFMessageHandler(dpid, reply));
alshabib5eb79392015-08-19 18:09:55 -0700406 break;
Marc De Leenheerb9311372015-07-09 11:36:49 -0700407 case EXPERIMENTER:
408 if (reply instanceof OFCalientFlowStatsReply) {
409 // Convert Calient flow statistics to regular flow stats
410 // TODO: parse remaining fields such as power levels etc. when we have proper monitoring API
sangyun-han69ed4462016-07-27 12:10:12 +0900411 if (sw == null) {
412 log.error("Switch {} is not found", dpid);
413 break;
414 }
415 OFFlowStatsReply.Builder fsr = sw.factory().buildFlowStatsReply();
Marc De Leenheerb9311372015-07-09 11:36:49 -0700416 List<OFFlowStatsEntry> entries = new LinkedList<>();
417 for (OFCalientFlowStatsEntry entry : ((OFCalientFlowStatsReply) msg).getEntries()) {
418
419 // Single instruction, i.e., output to port
420 OFActionOutput action = OFFactories
421 .getFactory(msg.getVersion())
422 .actions()
423 .buildOutput()
424 .setPort(entry.getOutPort())
425 .build();
426 OFInstruction instruction = OFFactories
427 .getFactory(msg.getVersion())
428 .instructions()
429 .applyActions(Collections.singletonList(action));
sangyun-han69ed4462016-07-27 12:10:12 +0900430 OFFlowStatsEntry fs = sw.factory().buildFlowStatsEntry()
Marc De Leenheerb9311372015-07-09 11:36:49 -0700431 .setMatch(entry.getMatch())
432 .setTableId(entry.getTableId())
433 .setDurationSec(entry.getDurationSec())
434 .setDurationNsec(entry.getDurationNsec())
435 .setPriority(entry.getPriority())
436 .setIdleTimeout(entry.getIdleTimeout())
437 .setHardTimeout(entry.getHardTimeout())
438 .setFlags(entry.getFlags())
439 .setCookie(entry.getCookie())
440 .setInstructions(Collections.singletonList(instruction))
441 .build();
442 entries.add(fs);
443 }
444 fsr.setEntries(entries);
445
446 flowStats = publishFlowStats(dpid, fsr.build());
447 if (flowStats != null) {
448 OFFlowStatsReply.Builder rep =
449 OFFactories.getFactory(msg.getVersion()).buildFlowStatsReply();
450 rep.setEntries(Lists.newLinkedList(flowStats));
Andrea Campanelladda93562016-03-02 11:08:12 -0800451 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
Marc De Leenheerb9311372015-07-09 11:36:49 -0700452 }
453 } else {
Andrea Campanelladda93562016-03-02 11:08:12 -0800454 executorMsgs.execute(new OFMessageHandler(dpid, reply));
Marc De Leenheerb9311372015-07-09 11:36:49 -0700455 }
456 break;
sangho6a0bb172015-02-05 12:24:48 -0800457 default:
alshabib5eb79392015-08-19 18:09:55 -0700458 log.warn("Discarding unknown stats reply type {}", reply.getStatsType());
Marc De Leenheerb9311372015-07-09 11:36:49 -0700459 break;
alshabib64def642014-12-02 23:27:37 -0800460 }
461 break;
alshabib8f1cf4a2014-09-17 14:44:48 -0700462 case BARRIER_REPLY:
Prince Pereirae7798032016-07-08 16:31:58 +0530463 if (errorMsgs.containsKey(msg.getXid())) {
464 //To make oferror msg handling and corresponding barrier reply serialized,
465 // executorErrorMsgs is used for both transaction
466 errorMsgs.remove(msg.getXid());
467 executorErrorMsgs.execute(new OFMessageHandler(dpid, msg));
468 } else {
469 executorBarrier.execute(new OFMessageHandler(dpid, msg));
470 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700471 break;
Marc De Leenheer631ffce2014-10-28 16:29:07 -0700472 case EXPERIMENTER:
sangyun-han69ed4462016-07-27 12:10:12 +0900473 if (sw == null) {
474 log.error("Switch {} is not found", dpid);
475 break;
476 }
Marc De Leenheerb9311372015-07-09 11:36:49 -0700477 long experimenter = ((OFExperimenter) msg).getExperimenter();
478 if (experimenter == 0x748771) {
479 // LINC-OE port stats
Marc De Leenheer631ffce2014-10-28 16:29:07 -0700480 OFCircuitPortStatus circuitPortStatus = (OFCircuitPortStatus) msg;
sangyun-han69ed4462016-07-27 12:10:12 +0900481 OFPortStatus.Builder portStatus = sw.factory().buildPortStatus();
482 OFPortDesc.Builder portDesc = sw.factory().buildPortDesc();
Marc De Leenheer631ffce2014-10-28 16:29:07 -0700483 portDesc.setPortNo(circuitPortStatus.getPortNo())
484 .setHwAddr(circuitPortStatus.getHwAddr())
485 .setName(circuitPortStatus.getName())
486 .setConfig(circuitPortStatus.getConfig())
487 .setState(circuitPortStatus.getState());
488 portStatus.setReason(circuitPortStatus.getReason()).setDesc(portDesc.build());
489 for (OpenFlowSwitchListener l : ofSwitchListener) {
490 l.portChanged(dpid, portStatus.build());
491 }
492 } else {
493 log.warn("Handling experimenter type {} not yet implemented",
494 ((OFExperimenter) msg).getExperimenter(), msg);
495 }
496 break;
tom7ef8ff92014-09-17 13:08:06 -0700497 default:
498 log.warn("Handling message type {} not yet implemented {}",
499 msg.getType(), msg);
500 }
501 }
502
sangho6a0bb172015-02-05 12:24:48 -0800503 private synchronized Collection<OFFlowStatsEntry> publishFlowStats(Dpid dpid,
504 OFFlowStatsReply reply) {
alshabib64def642014-12-02 23:27:37 -0800505 //TODO: Get rid of synchronized
sangho6a0bb172015-02-05 12:24:48 -0800506 fullFlowStats.putAll(dpid, reply.getEntries());
alshabib64def642014-12-02 23:27:37 -0800507 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
sangho6a0bb172015-02-05 12:24:48 -0800508 return fullFlowStats.removeAll(dpid);
509 }
510 return null;
511 }
512
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700513 private synchronized Collection<OFTableStatsEntry> publishTableStats(Dpid dpid,
514 OFTableStatsReply reply) {
515 //TODO: Get rid of synchronized
516 fullTableStats.putAll(dpid, reply.getEntries());
517 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
518 return fullTableStats.removeAll(dpid);
519 }
520 return null;
521 }
522
sangho6a0bb172015-02-05 12:24:48 -0800523 private synchronized Collection<OFGroupStatsEntry> publishGroupStats(Dpid dpid,
524 OFGroupStatsReply reply) {
525 //TODO: Get rid of synchronized
526 fullGroupStats.putAll(dpid, reply.getEntries());
527 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
528 return fullGroupStats.removeAll(dpid);
529 }
530 return null;
531 }
532
533 private synchronized Collection<OFGroupDescStatsEntry> publishGroupDescStats(Dpid dpid,
534 OFGroupDescStatsReply reply) {
535 //TODO: Get rid of synchronized
536 fullGroupDescStats.putAll(dpid, reply.getEntries());
537 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
538 return fullGroupDescStats.removeAll(dpid);
alshabib64def642014-12-02 23:27:37 -0800539 }
540 return null;
541 }
542
sangho538108b2015-04-08 14:29:20 -0700543 private synchronized Collection<OFPortStatsEntry> publishPortStats(Dpid dpid,
544 OFPortStatsReply reply) {
545 fullPortStats.putAll(dpid, reply.getEntries());
546 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
547 return fullPortStats.removeAll(dpid);
548 }
549 return null;
550 }
551
tom7ef8ff92014-09-17 13:08:06 -0700552 @Override
553 public void setRole(Dpid dpid, RoleState role) {
Yuta HIGUCHI79cbd1c2014-10-02 16:57:57 -0700554 final OpenFlowSwitch sw = getSwitch(dpid);
555 if (sw == null) {
556 log.debug("Switch not connected. Ignoring setRole({}, {})", dpid, role);
557 return;
558 }
559 sw.setRole(role);
tom7ef8ff92014-09-17 13:08:06 -0700560 }
561
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700562 class InternalDeviceListener implements DeviceListener {
563
564 @Override
565 public boolean isRelevant(DeviceEvent event) {
Hyunsun Moone71494d2016-11-15 14:45:25 -0800566 return event.subject().type() != CONTROLLER && event.type() == DEVICE_REMOVED;
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700567 }
568
569 @Override
570 public void event(DeviceEvent event) {
571 switch (event.type()) {
572 case DEVICE_ADDED:
573 break;
574 case DEVICE_AVAILABILITY_CHANGED:
575 break;
576 case DEVICE_REMOVED:
577 // Device administratively removed, disconnect
578 Optional.ofNullable(getSwitch(dpid(event.subject().id().uri())))
579 .ifPresent(OpenFlowSwitch::disconnectSwitch);
580 break;
581 case DEVICE_SUSPENDED:
582 break;
583 case DEVICE_UPDATED:
584 break;
585 case PORT_ADDED:
586 break;
587 case PORT_REMOVED:
588 break;
589 case PORT_STATS_UPDATED:
590 break;
591 case PORT_UPDATED:
592 break;
593 default:
594 break;
595
596 }
597
598 }
599
600 }
601
tom7ef8ff92014-09-17 13:08:06 -0700602 /**
603 * Implementation of an OpenFlow Agent which is responsible for
604 * keeping track of connected switches and the state in which
605 * they are.
606 */
607 public class OpenFlowSwitchAgent implements OpenFlowAgent {
608
609 private final Logger log = LoggerFactory.getLogger(OpenFlowSwitchAgent.class);
610 private final Lock switchLock = new ReentrantLock();
611
612 @Override
613 public boolean addConnectedSwitch(Dpid dpid, OpenFlowSwitch sw) {
alshabib9eab22f2014-10-20 17:17:31 -0700614
tom7ef8ff92014-09-17 13:08:06 -0700615 if (connectedSwitches.get(dpid) != null) {
616 log.error("Trying to add connectedSwitch but found a previous "
617 + "value for dpid: {}", dpid);
618 return false;
619 } else {
Yuta HIGUCHIeb3f30b2014-10-22 11:34:49 -0700620 log.info("Added switch {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700621 connectedSwitches.put(dpid, sw);
alshabib8f1cf4a2014-09-17 14:44:48 -0700622 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700623 l.switchAdded(dpid);
624 }
625 return true;
626 }
627 }
628
629 @Override
630 public boolean validActivation(Dpid dpid) {
631 if (connectedSwitches.get(dpid) == null) {
632 log.error("Trying to activate switch but is not in "
633 + "connected switches: dpid {}. Aborting ..",
634 dpid);
635 return false;
636 }
637 if (activeMasterSwitches.get(dpid) != null ||
638 activeEqualSwitches.get(dpid) != null) {
639 log.error("Trying to activate switch but it is already "
640 + "activated: dpid {}. Found in activeMaster: {} "
Ray Milkey6bc43c22015-11-06 13:22:38 -0800641 + "Found in activeEqual: {}. Aborting ..",
642 dpid,
643 (activeMasterSwitches.get(dpid) == null) ? 'N' : 'Y',
644 (activeEqualSwitches.get(dpid) == null) ? 'N' : 'Y');
tom7ef8ff92014-09-17 13:08:06 -0700645 return false;
646 }
647 return true;
648 }
649
650
651 @Override
652 public boolean addActivatedMasterSwitch(Dpid dpid, OpenFlowSwitch sw) {
653 switchLock.lock();
654 try {
655 if (!validActivation(dpid)) {
656 return false;
657 }
658 activeMasterSwitches.put(dpid, sw);
659 return true;
660 } finally {
661 switchLock.unlock();
662 }
663 }
664
665 @Override
666 public boolean addActivatedEqualSwitch(Dpid dpid, OpenFlowSwitch sw) {
667 switchLock.lock();
668 try {
669 if (!validActivation(dpid)) {
670 return false;
671 }
672 activeEqualSwitches.put(dpid, sw);
673 log.info("Added Activated EQUAL Switch {}", dpid);
674 return true;
675 } finally {
676 switchLock.unlock();
677 }
678 }
679
680 @Override
681 public void transitionToMasterSwitch(Dpid dpid) {
682 switchLock.lock();
683 try {
684 if (activeMasterSwitches.containsKey(dpid)) {
685 return;
686 }
687 OpenFlowSwitch sw = activeEqualSwitches.remove(dpid);
688 if (sw == null) {
689 sw = getSwitch(dpid);
690 if (sw == null) {
691 log.error("Transition to master called on sw {}, but switch "
692 + "was not found in controller-cache", dpid);
693 return;
694 }
695 }
696 log.info("Transitioned switch {} to MASTER", dpid);
697 activeMasterSwitches.put(dpid, sw);
698 } finally {
699 switchLock.unlock();
700 }
701 }
702
703
704 @Override
705 public void transitionToEqualSwitch(Dpid dpid) {
706 switchLock.lock();
707 try {
708 if (activeEqualSwitches.containsKey(dpid)) {
709 return;
710 }
711 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
712 if (sw == null) {
713 sw = getSwitch(dpid);
714 if (sw == null) {
715 log.error("Transition to equal called on sw {}, but switch "
716 + "was not found in controller-cache", dpid);
717 return;
718 }
719 }
720 log.info("Transitioned switch {} to EQUAL", dpid);
721 activeEqualSwitches.put(dpid, sw);
722 } finally {
723 switchLock.unlock();
724 }
725
726 }
727
728 @Override
729 public void removeConnectedSwitch(Dpid dpid) {
730 connectedSwitches.remove(dpid);
731 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
732 if (sw == null) {
Thomas Vachuska3358af22015-05-19 18:40:34 -0700733 log.debug("sw was null for {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700734 sw = activeEqualSwitches.remove(dpid);
735 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700736 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700737 l.switchRemoved(dpid);
738 }
739 }
740
741 @Override
Jian Lia78cdb22016-04-21 13:03:58 -0700742 public void processDownstreamMessage(Dpid dpid, List<OFMessage> m) {
743 for (OpenFlowMessageListener listener : ofMessageListener) {
744 listener.handleOutgoingMessage(dpid, m);
745 }
746 }
747
748
749 @Override
tom7ef8ff92014-09-17 13:08:06 -0700750 public void processMessage(Dpid dpid, OFMessage m) {
751 processPacket(dpid, m);
Jian Lia78cdb22016-04-21 13:03:58 -0700752
753 for (OpenFlowMessageListener listener : ofMessageListener) {
754 listener.handleIncomingMessage(dpid, m);
755 }
tom7ef8ff92014-09-17 13:08:06 -0700756 }
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700757
758 @Override
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700759 public void returnRoleReply(Dpid dpid, RoleState requested, RoleState response) {
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700760 for (OpenFlowSwitchListener l : ofSwitchListener) {
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700761 l.receivedRoleReply(dpid, requested, response);
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700762 }
763 }
tom7ef8ff92014-09-17 13:08:06 -0700764 }
765
Jian Li152b8852015-12-07 14:47:25 -0800766 /**
Jian Li2266bff2016-04-21 11:01:25 -0700767 * OpenFlow message handler.
Jian Li152b8852015-12-07 14:47:25 -0800768 */
Ray Milkey7ec0d1b2015-11-13 08:51:35 -0800769 protected final class OFMessageHandler implements Runnable {
alshabib8f1cf4a2014-09-17 14:44:48 -0700770
Ray Milkey7ec0d1b2015-11-13 08:51:35 -0800771 protected final OFMessage msg;
772 protected final Dpid dpid;
alshabib8f1cf4a2014-09-17 14:44:48 -0700773
774 public OFMessageHandler(Dpid dpid, OFMessage msg) {
775 this.msg = msg;
776 this.dpid = dpid;
777 }
778
779 @Override
780 public void run() {
alshabibeec3a062014-09-17 18:01:26 -0700781 for (OpenFlowEventListener listener : ofEventListener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700782 listener.handleMessage(dpid, msg);
783 }
784 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700785 }
tom7ef8ff92014-09-17 13:08:06 -0700786}