blob: cd4548d01109ce27a576b574d9ec4c3915206a44 [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;
alshabibb452fd72015-04-22 20:46:20 -070034import org.onosproject.net.driver.DriverService;
Brian O'Connorabafb502014-12-02 22:26:20 -080035import org.onosproject.openflow.controller.DefaultOpenFlowPacketContext;
36import org.onosproject.openflow.controller.Dpid;
37import org.onosproject.openflow.controller.OpenFlowController;
38import org.onosproject.openflow.controller.OpenFlowEventListener;
Jian Lia78cdb22016-04-21 13:03:58 -070039import org.onosproject.openflow.controller.OpenFlowMessageListener;
Brian O'Connorabafb502014-12-02 22:26:20 -080040import org.onosproject.openflow.controller.OpenFlowPacketContext;
41import org.onosproject.openflow.controller.OpenFlowSwitch;
42import org.onosproject.openflow.controller.OpenFlowSwitchListener;
43import org.onosproject.openflow.controller.PacketListener;
44import org.onosproject.openflow.controller.RoleState;
45import org.onosproject.openflow.controller.driver.OpenFlowAgent;
Jonathan Hartbbd91d42015-02-27 11:18:04 -080046import org.osgi.service.component.ComponentContext;
Marc De Leenheerb9311372015-07-09 11:36:49 -070047import org.projectfloodlight.openflow.protocol.OFCalientFlowStatsEntry;
48import org.projectfloodlight.openflow.protocol.OFCalientFlowStatsReply;
Marc De Leenheer631ffce2014-10-28 16:29:07 -070049import org.projectfloodlight.openflow.protocol.OFCircuitPortStatus;
50import org.projectfloodlight.openflow.protocol.OFExperimenter;
alshabib64def642014-12-02 23:27:37 -080051import org.projectfloodlight.openflow.protocol.OFFactories;
52import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry;
53import org.projectfloodlight.openflow.protocol.OFFlowStatsReply;
sangho6a0bb172015-02-05 12:24:48 -080054import org.projectfloodlight.openflow.protocol.OFGroupDescStatsEntry;
55import org.projectfloodlight.openflow.protocol.OFGroupDescStatsReply;
56import org.projectfloodlight.openflow.protocol.OFGroupStatsEntry;
57import org.projectfloodlight.openflow.protocol.OFGroupStatsReply;
tom7ef8ff92014-09-17 13:08:06 -070058import org.projectfloodlight.openflow.protocol.OFMessage;
59import org.projectfloodlight.openflow.protocol.OFPacketIn;
Marc De Leenheer631ffce2014-10-28 16:29:07 -070060import org.projectfloodlight.openflow.protocol.OFPortDesc;
sangho538108b2015-04-08 14:29:20 -070061import org.projectfloodlight.openflow.protocol.OFPortStatsEntry;
62import org.projectfloodlight.openflow.protocol.OFPortStatsReply;
tom7ef8ff92014-09-17 13:08:06 -070063import org.projectfloodlight.openflow.protocol.OFPortStatus;
Ayaka Koshibe38594c22014-10-22 13:36:12 -070064import org.projectfloodlight.openflow.protocol.OFStatsReply;
alshabib64def642014-12-02 23:27:37 -080065import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
Jian Li2266bff2016-04-21 11:01:25 -070066import org.projectfloodlight.openflow.protocol.OFTableStatsEntry;
67import org.projectfloodlight.openflow.protocol.OFTableStatsReply;
Marc De Leenheerb9311372015-07-09 11:36:49 -070068import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
69import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
tom7ef8ff92014-09-17 13:08:06 -070070import org.slf4j.Logger;
71import org.slf4j.LoggerFactory;
72
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080073import java.util.Collection;
Marc De Leenheerb9311372015-07-09 11:36:49 -070074import java.util.Collections;
Marc De Leenheerb9311372015-07-09 11:36:49 -070075import java.util.LinkedList;
76import java.util.List;
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -070077import java.util.Optional;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080078import java.util.Set;
Marc De Leenheer8aba62f2017-04-25 14:33:37 -070079import java.util.concurrent.CompletableFuture;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080080import java.util.concurrent.ConcurrentHashMap;
HIGUCHI Yuta1979f552015-12-28 21:24:26 -080081import java.util.concurrent.ConcurrentMap;
Jonathan Hart6d44d192015-05-11 18:01:19 -070082import java.util.concurrent.CopyOnWriteArraySet;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080083import java.util.concurrent.ExecutorService;
84import java.util.concurrent.Executors;
85import java.util.concurrent.locks.Lock;
86import java.util.concurrent.locks.ReentrantLock;
Jian Li2266bff2016-04-21 11:01:25 -070087
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080088import static org.onlab.util.Tools.groupedThreads;
Hyunsun Moone71494d2016-11-15 14:45:25 -080089import static org.onosproject.net.Device.Type.CONTROLLER;
90import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_REMOVED;
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -070091import static org.onosproject.openflow.controller.Dpid.dpid;
tom7ef8ff92014-09-17 13:08:06 -070092
93@Component(immediate = true)
94@Service
95public class OpenFlowControllerImpl implements OpenFlowController {
Charles Chanecfdfb72015-11-24 19:05:50 -080096 private static final String APP_ID = "org.onosproject.openflow-base";
Brian O'Connorff278502015-09-22 14:49:52 -070097 private static final String DEFAULT_OFPORT = "6633,6653";
Yuta HIGUCHI8552b172016-07-25 12:10:08 -070098 private static final int DEFAULT_WORKER_THREADS = 0;
tom7ef8ff92014-09-17 13:08:06 -070099
100 private static final Logger log =
101 LoggerFactory.getLogger(OpenFlowControllerImpl.class);
102
alshabibb452fd72015-04-22 20:46:20 -0700103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Charles Chanecfdfb72015-11-24 19:05:50 -0800104 protected CoreService coreService;
105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabibb452fd72015-04-22 20:46:20 -0700107 protected DriverService driverService;
108
Charles Chan3b00e1b2015-08-26 23:12:52 +0800109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
110 protected ComponentConfigService cfgService;
111
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
113 protected DeviceService deviceService;
114
115
Brian O'Connore2a399e2015-09-22 15:32:50 -0700116 @Property(name = "openflowPorts", value = DEFAULT_OFPORT,
117 label = "Port numbers (comma separated) used by OpenFlow protocol; default is 6633,6653")
118 private String openflowPorts = DEFAULT_OFPORT;
Charles Chan3b00e1b2015-08-26 23:12:52 +0800119
120 @Property(name = "workerThreads", intValue = DEFAULT_WORKER_THREADS,
Yuta HIGUCHI8552b172016-07-25 12:10:08 -0700121 label = "Number of controller worker threads")
Charles Chan3b00e1b2015-08-26 23:12:52 +0800122 private int workerThreads = DEFAULT_WORKER_THREADS;
123
Ray Milkey7ec0d1b2015-11-13 08:51:35 -0800124 protected ExecutorService executorMsgs =
Andrea Campanelladda93562016-03-02 11:08:12 -0800125 Executors.newFixedThreadPool(32, groupedThreads("onos/of", "event-stats-%d", log));
Pavlin Radoslavov369c6432014-12-03 16:25:14 -0800126
127 private final ExecutorService executorBarrier =
Andrea Campanelladda93562016-03-02 11:08:12 -0800128 Executors.newFixedThreadPool(4, groupedThreads("onos/of", "event-barrier-%d", log));
alshabib8f1cf4a2014-09-17 14:44:48 -0700129
Prince Pereirae7798032016-07-08 16:31:58 +0530130 //Separate executor thread for handling error messages and barrier replies for same failed
131 // transactions to avoid context switching of thread
132 protected ExecutorService executorErrorMsgs =
133 Executors.newSingleThreadExecutor(groupedThreads("onos/of", "event-error-msg-%d", log));
134
135 //concurrent hashmap to track failed transactions
136 protected ConcurrentMap<Long, Boolean> errorMsgs =
137 new ConcurrentHashMap<>();
HIGUCHI Yuta1979f552015-12-28 21:24:26 -0800138 protected ConcurrentMap<Dpid, OpenFlowSwitch> connectedSwitches =
Jonathan Hart6d44d192015-05-11 18:01:19 -0700139 new ConcurrentHashMap<>();
HIGUCHI Yuta1979f552015-12-28 21:24:26 -0800140 protected ConcurrentMap<Dpid, OpenFlowSwitch> activeMasterSwitches =
Jonathan Hart6d44d192015-05-11 18:01:19 -0700141 new ConcurrentHashMap<>();
HIGUCHI Yuta1979f552015-12-28 21:24:26 -0800142 protected ConcurrentMap<Dpid, OpenFlowSwitch> activeEqualSwitches =
Jonathan Hart6d44d192015-05-11 18:01:19 -0700143 new ConcurrentHashMap<>();
tom7ef8ff92014-09-17 13:08:06 -0700144
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700145 // Key: dpid, value: map with key: long (XID), value: completable future
146 protected ConcurrentMap<Dpid, ConcurrentMap<Long, CompletableFuture<OFMessage>>> responses =
147 new ConcurrentHashMap<>();
148
tom7ef8ff92014-09-17 13:08:06 -0700149 protected OpenFlowSwitchAgent agent = new OpenFlowSwitchAgent();
Jonathan Hart6d44d192015-05-11 18:01:19 -0700150 protected Set<OpenFlowSwitchListener> ofSwitchListener = new CopyOnWriteArraySet<>();
tom7ef8ff92014-09-17 13:08:06 -0700151
152 protected Multimap<Integer, PacketListener> ofPacketListener =
153 ArrayListMultimap.create();
154
Jonathan Hart6d44d192015-05-11 18:01:19 -0700155 protected Set<OpenFlowEventListener> ofEventListener = new CopyOnWriteArraySet<>();
tom7ef8ff92014-09-17 13:08:06 -0700156
Jian Lia78cdb22016-04-21 13:03:58 -0700157 protected Set<OpenFlowMessageListener> ofMessageListener = new CopyOnWriteArraySet<>();
Jian Li28247b52016-01-07 17:24:15 -0800158
sangho6a0bb172015-02-05 12:24:48 -0800159 protected Multimap<Dpid, OFFlowStatsEntry> fullFlowStats =
160 ArrayListMultimap.create();
161
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700162 protected Multimap<Dpid, OFTableStatsEntry> fullTableStats =
163 ArrayListMultimap.create();
164
sangho6a0bb172015-02-05 12:24:48 -0800165 protected Multimap<Dpid, OFGroupStatsEntry> fullGroupStats =
166 ArrayListMultimap.create();
167
168 protected Multimap<Dpid, OFGroupDescStatsEntry> fullGroupDescStats =
alshabib64def642014-12-02 23:27:37 -0800169 ArrayListMultimap.create();
170
sangho538108b2015-04-08 14:29:20 -0700171 protected Multimap<Dpid, OFPortStatsEntry> fullPortStats =
172 ArrayListMultimap.create();
173
tom7ef8ff92014-09-17 13:08:06 -0700174 private final Controller ctrl = new Controller();
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700175 private InternalDeviceListener listener = new InternalDeviceListener();
tom7ef8ff92014-09-17 13:08:06 -0700176
177 @Activate
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800178 public void activate(ComponentContext context) {
Andrea Campanella3556f362016-04-28 15:18:10 -0700179 coreService.registerApplication(APP_ID, this::cleanup);
Charles Chan3b00e1b2015-08-26 23:12:52 +0800180 cfgService.registerProperties(getClass());
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700181 deviceService.addListener(listener);
Brian O'Connorff278502015-09-22 14:49:52 -0700182 ctrl.setConfigParams(context.getProperties());
alshabibb452fd72015-04-22 20:46:20 -0700183 ctrl.start(agent, driverService);
tom7ef8ff92014-09-17 13:08:06 -0700184 }
185
Andrea Campanella3556f362016-04-28 15:18:10 -0700186 private void cleanup() {
187 // Close listening channel and all OF channels. Clean information about switches
188 // before deactivating
Charles Chanecfdfb72015-11-24 19:05:50 -0800189 ctrl.stop();
190 connectedSwitches.values().forEach(OpenFlowSwitch::disconnectSwitch);
Andrea Campanella3556f362016-04-28 15:18:10 -0700191 connectedSwitches.clear();
192 activeMasterSwitches.clear();
193 activeEqualSwitches.clear();
Charles Chanecfdfb72015-11-24 19:05:50 -0800194 }
195
tom7ef8ff92014-09-17 13:08:06 -0700196 @Deactivate
197 public void deactivate() {
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700198 deviceService.removeListener(listener);
Thiago Santos61725402016-08-05 17:58:56 -0300199 cleanup();
Charles Chan3b00e1b2015-08-26 23:12:52 +0800200 cfgService.unregisterProperties(getClass(), false);
tom7ef8ff92014-09-17 13:08:06 -0700201 }
202
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800203 @Modified
204 public void modified(ComponentContext context) {
Charles Chan3b00e1b2015-08-26 23:12:52 +0800205 ctrl.stop();
Brian O'Connorff278502015-09-22 14:49:52 -0700206 ctrl.setConfigParams(context.getProperties());
Charles Chan3b00e1b2015-08-26 23:12:52 +0800207 ctrl.start(agent, driverService);
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800208 }
209
tom7ef8ff92014-09-17 13:08:06 -0700210 @Override
211 public Iterable<OpenFlowSwitch> getSwitches() {
212 return connectedSwitches.values();
213 }
214
215 @Override
216 public Iterable<OpenFlowSwitch> getMasterSwitches() {
217 return activeMasterSwitches.values();
218 }
219
220 @Override
221 public Iterable<OpenFlowSwitch> getEqualSwitches() {
222 return activeEqualSwitches.values();
223 }
224
225 @Override
226 public OpenFlowSwitch getSwitch(Dpid dpid) {
227 return connectedSwitches.get(dpid);
228 }
229
230 @Override
231 public OpenFlowSwitch getMasterSwitch(Dpid dpid) {
232 return activeMasterSwitches.get(dpid);
233 }
234
235 @Override
236 public OpenFlowSwitch getEqualSwitch(Dpid dpid) {
237 return activeEqualSwitches.get(dpid);
238 }
239
240 @Override
241 public void addListener(OpenFlowSwitchListener listener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700242 if (!ofSwitchListener.contains(listener)) {
243 this.ofSwitchListener.add(listener);
tom7ef8ff92014-09-17 13:08:06 -0700244 }
245 }
246
247 @Override
248 public void removeListener(OpenFlowSwitchListener listener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700249 this.ofSwitchListener.remove(listener);
tom7ef8ff92014-09-17 13:08:06 -0700250 }
251
252 @Override
Jian Lia78cdb22016-04-21 13:03:58 -0700253 public void addMessageListener(OpenFlowMessageListener listener) {
254 ofMessageListener.add(listener);
255 }
256
257 @Override
258 public void removeMessageListener(OpenFlowMessageListener listener) {
259 ofMessageListener.remove(listener);
260 }
261
262 @Override
tom7ef8ff92014-09-17 13:08:06 -0700263 public void addPacketListener(int priority, PacketListener listener) {
264 ofPacketListener.put(priority, listener);
265 }
266
267 @Override
268 public void removePacketListener(PacketListener listener) {
269 ofPacketListener.values().remove(listener);
270 }
271
272 @Override
alshabibeec3a062014-09-17 18:01:26 -0700273 public void addEventListener(OpenFlowEventListener listener) {
274 ofEventListener.add(listener);
275 }
276
277 @Override
278 public void removeEventListener(OpenFlowEventListener listener) {
279 ofEventListener.remove(listener);
280 }
281
282 @Override
tom7ef8ff92014-09-17 13:08:06 -0700283 public void write(Dpid dpid, OFMessage msg) {
284 this.getSwitch(dpid).sendMsg(msg);
285 }
286
287 @Override
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700288 public CompletableFuture<OFMessage> writeResponse(Dpid dpid, OFMessage msg) {
289 write(dpid, msg);
290
291 ConcurrentMap<Long, CompletableFuture<OFMessage>> xids =
292 responses.computeIfAbsent(dpid, k -> new ConcurrentHashMap());
293
294 CompletableFuture<OFMessage> future = new CompletableFuture();
295 xids.put(msg.getXid(), future);
296
297 return future;
298 }
299
300 @Override
tom7ef8ff92014-09-17 13:08:06 -0700301 public void processPacket(Dpid dpid, OFMessage msg) {
sangho6a0bb172015-02-05 12:24:48 -0800302 Collection<OFFlowStatsEntry> flowStats;
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700303 Collection<OFTableStatsEntry> tableStats;
sangho6a0bb172015-02-05 12:24:48 -0800304 Collection<OFGroupStatsEntry> groupStats;
305 Collection<OFGroupDescStatsEntry> groupDescStats;
sangho538108b2015-04-08 14:29:20 -0700306 Collection<OFPortStatsEntry> portStats;
sangho6a0bb172015-02-05 12:24:48 -0800307
sangyun-han69ed4462016-07-27 12:10:12 +0900308 OpenFlowSwitch sw = this.getSwitch(dpid);
309
Marc De Leenheer8aba62f2017-04-25 14:33:37 -0700310 // Check if someone is waiting for this message
311 ConcurrentMap<Long, CompletableFuture<OFMessage>> xids = responses.get(dpid);
312 if (xids != null && xids.containsKey(msg.getXid())) {
313 xids.remove(msg.getXid()).complete(msg);
314 }
315
tom7ef8ff92014-09-17 13:08:06 -0700316 switch (msg.getType()) {
317 case PORT_STATUS:
alshabib8f1cf4a2014-09-17 14:44:48 -0700318 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700319 l.portChanged(dpid, (OFPortStatus) msg);
320 }
321 break;
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700322 case FEATURES_REPLY:
323 for (OpenFlowSwitchListener l : ofSwitchListener) {
324 l.switchChanged(dpid);
325 }
326 break;
tom7ef8ff92014-09-17 13:08:06 -0700327 case PACKET_IN:
sangyun-han69ed4462016-07-27 12:10:12 +0900328 if (sw == null) {
329 log.error("Switch {} is not found", dpid);
330 break;
331 }
tom7ef8ff92014-09-17 13:08:06 -0700332 OpenFlowPacketContext pktCtx = DefaultOpenFlowPacketContext
sangyun-han69ed4462016-07-27 12:10:12 +0900333 .packetContextFromPacketIn(sw, (OFPacketIn) msg);
tom7ef8ff92014-09-17 13:08:06 -0700334 for (PacketListener p : ofPacketListener.values()) {
335 p.handlePacket(pktCtx);
336 }
337 break;
Pavlin Radoslavov369c6432014-12-03 16:25:14 -0800338 // TODO: Consider using separate threadpool for sensitive messages.
339 // ie. Back to back error could cause us to starve.
340 case FLOW_REMOVED:
Andrea Campanelladda93562016-03-02 11:08:12 -0800341 executorMsgs.execute(new OFMessageHandler(dpid, msg));
Pavlin Radoslavov369c6432014-12-03 16:25:14 -0800342 break;
Prince Pereirae7798032016-07-08 16:31:58 +0530343 case ERROR:
344 log.debug("Received error message from {}: {}", dpid, msg);
345 errorMsgs.putIfAbsent(msg.getXid(), true);
346 executorErrorMsgs.execute(new OFMessageHandler(dpid, msg));
347 break;
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700348 case STATS_REPLY:
349 OFStatsReply reply = (OFStatsReply) msg;
sangho6a0bb172015-02-05 12:24:48 -0800350 switch (reply.getStatsType()) {
351 case PORT_DESC:
352 for (OpenFlowSwitchListener l : ofSwitchListener) {
353 l.switchChanged(dpid);
354 }
355 break;
356 case FLOW:
357 flowStats = publishFlowStats(dpid, (OFFlowStatsReply) reply);
358 if (flowStats != null) {
359 OFFlowStatsReply.Builder rep =
360 OFFactories.getFactory(msg.getVersion()).buildFlowStatsReply();
361 rep.setEntries(Lists.newLinkedList(flowStats));
ssyoon9030fbcd92015-08-17 10:42:07 +0900362 rep.setXid(reply.getXid());
Andrea Campanelladda93562016-03-02 11:08:12 -0800363 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
sangho6a0bb172015-02-05 12:24:48 -0800364 }
365 break;
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700366 case TABLE:
367 tableStats = publishTableStats(dpid, (OFTableStatsReply) reply);
368 if (tableStats != null) {
369 OFTableStatsReply.Builder rep =
370 OFFactories.getFactory(msg.getVersion()).buildTableStatsReply();
371 rep.setEntries(Lists.newLinkedList(tableStats));
Andrea Campanelladda93562016-03-02 11:08:12 -0800372 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700373 }
374 break;
sangho6a0bb172015-02-05 12:24:48 -0800375 case GROUP:
376 groupStats = publishGroupStats(dpid, (OFGroupStatsReply) reply);
377 if (groupStats != null) {
378 OFGroupStatsReply.Builder rep =
379 OFFactories.getFactory(msg.getVersion()).buildGroupStatsReply();
380 rep.setEntries(Lists.newLinkedList(groupStats));
381 rep.setXid(reply.getXid());
Andrea Campanelladda93562016-03-02 11:08:12 -0800382 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
sangho6a0bb172015-02-05 12:24:48 -0800383 }
384 break;
385 case GROUP_DESC:
386 groupDescStats = publishGroupDescStats(dpid,
387 (OFGroupDescStatsReply) reply);
388 if (groupDescStats != null) {
389 OFGroupDescStatsReply.Builder rep =
Marc De Leenheerb9311372015-07-09 11:36:49 -0700390 OFFactories.getFactory(msg.getVersion()).buildGroupDescStatsReply();
sangho6a0bb172015-02-05 12:24:48 -0800391 rep.setEntries(Lists.newLinkedList(groupDescStats));
392 rep.setXid(reply.getXid());
Andrea Campanelladda93562016-03-02 11:08:12 -0800393 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
sangho6a0bb172015-02-05 12:24:48 -0800394 }
395 break;
sangho538108b2015-04-08 14:29:20 -0700396 case PORT:
Andrea Campanelladda93562016-03-02 11:08:12 -0800397 executorMsgs.execute(new OFMessageHandler(dpid, reply));
sangho538108b2015-04-08 14:29:20 -0700398 break;
alshabib5eb79392015-08-19 18:09:55 -0700399 case METER:
Andrea Campanelladda93562016-03-02 11:08:12 -0800400 executorMsgs.execute(new OFMessageHandler(dpid, reply));
alshabib5eb79392015-08-19 18:09:55 -0700401 break;
Marc De Leenheerb9311372015-07-09 11:36:49 -0700402 case EXPERIMENTER:
403 if (reply instanceof OFCalientFlowStatsReply) {
404 // Convert Calient flow statistics to regular flow stats
405 // TODO: parse remaining fields such as power levels etc. when we have proper monitoring API
sangyun-han69ed4462016-07-27 12:10:12 +0900406 if (sw == null) {
407 log.error("Switch {} is not found", dpid);
408 break;
409 }
410 OFFlowStatsReply.Builder fsr = sw.factory().buildFlowStatsReply();
Marc De Leenheerb9311372015-07-09 11:36:49 -0700411 List<OFFlowStatsEntry> entries = new LinkedList<>();
412 for (OFCalientFlowStatsEntry entry : ((OFCalientFlowStatsReply) msg).getEntries()) {
413
414 // Single instruction, i.e., output to port
415 OFActionOutput action = OFFactories
416 .getFactory(msg.getVersion())
417 .actions()
418 .buildOutput()
419 .setPort(entry.getOutPort())
420 .build();
421 OFInstruction instruction = OFFactories
422 .getFactory(msg.getVersion())
423 .instructions()
424 .applyActions(Collections.singletonList(action));
sangyun-han69ed4462016-07-27 12:10:12 +0900425 OFFlowStatsEntry fs = sw.factory().buildFlowStatsEntry()
Marc De Leenheerb9311372015-07-09 11:36:49 -0700426 .setMatch(entry.getMatch())
427 .setTableId(entry.getTableId())
428 .setDurationSec(entry.getDurationSec())
429 .setDurationNsec(entry.getDurationNsec())
430 .setPriority(entry.getPriority())
431 .setIdleTimeout(entry.getIdleTimeout())
432 .setHardTimeout(entry.getHardTimeout())
433 .setFlags(entry.getFlags())
434 .setCookie(entry.getCookie())
435 .setInstructions(Collections.singletonList(instruction))
436 .build();
437 entries.add(fs);
438 }
439 fsr.setEntries(entries);
440
441 flowStats = publishFlowStats(dpid, fsr.build());
442 if (flowStats != null) {
443 OFFlowStatsReply.Builder rep =
444 OFFactories.getFactory(msg.getVersion()).buildFlowStatsReply();
445 rep.setEntries(Lists.newLinkedList(flowStats));
Andrea Campanelladda93562016-03-02 11:08:12 -0800446 executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
Marc De Leenheerb9311372015-07-09 11:36:49 -0700447 }
448 } else {
Andrea Campanelladda93562016-03-02 11:08:12 -0800449 executorMsgs.execute(new OFMessageHandler(dpid, reply));
Marc De Leenheerb9311372015-07-09 11:36:49 -0700450 }
451 break;
sangho6a0bb172015-02-05 12:24:48 -0800452 default:
alshabib5eb79392015-08-19 18:09:55 -0700453 log.warn("Discarding unknown stats reply type {}", reply.getStatsType());
Marc De Leenheerb9311372015-07-09 11:36:49 -0700454 break;
alshabib64def642014-12-02 23:27:37 -0800455 }
456 break;
alshabib8f1cf4a2014-09-17 14:44:48 -0700457 case BARRIER_REPLY:
Prince Pereirae7798032016-07-08 16:31:58 +0530458 if (errorMsgs.containsKey(msg.getXid())) {
459 //To make oferror msg handling and corresponding barrier reply serialized,
460 // executorErrorMsgs is used for both transaction
461 errorMsgs.remove(msg.getXid());
462 executorErrorMsgs.execute(new OFMessageHandler(dpid, msg));
463 } else {
464 executorBarrier.execute(new OFMessageHandler(dpid, msg));
465 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700466 break;
Marc De Leenheer631ffce2014-10-28 16:29:07 -0700467 case EXPERIMENTER:
sangyun-han69ed4462016-07-27 12:10:12 +0900468 if (sw == null) {
469 log.error("Switch {} is not found", dpid);
470 break;
471 }
Marc De Leenheerb9311372015-07-09 11:36:49 -0700472 long experimenter = ((OFExperimenter) msg).getExperimenter();
473 if (experimenter == 0x748771) {
474 // LINC-OE port stats
Marc De Leenheer631ffce2014-10-28 16:29:07 -0700475 OFCircuitPortStatus circuitPortStatus = (OFCircuitPortStatus) msg;
sangyun-han69ed4462016-07-27 12:10:12 +0900476 OFPortStatus.Builder portStatus = sw.factory().buildPortStatus();
477 OFPortDesc.Builder portDesc = sw.factory().buildPortDesc();
Marc De Leenheer631ffce2014-10-28 16:29:07 -0700478 portDesc.setPortNo(circuitPortStatus.getPortNo())
479 .setHwAddr(circuitPortStatus.getHwAddr())
480 .setName(circuitPortStatus.getName())
481 .setConfig(circuitPortStatus.getConfig())
482 .setState(circuitPortStatus.getState());
483 portStatus.setReason(circuitPortStatus.getReason()).setDesc(portDesc.build());
484 for (OpenFlowSwitchListener l : ofSwitchListener) {
485 l.portChanged(dpid, portStatus.build());
486 }
487 } else {
488 log.warn("Handling experimenter type {} not yet implemented",
489 ((OFExperimenter) msg).getExperimenter(), msg);
490 }
491 break;
tom7ef8ff92014-09-17 13:08:06 -0700492 default:
493 log.warn("Handling message type {} not yet implemented {}",
494 msg.getType(), msg);
495 }
496 }
497
sangho6a0bb172015-02-05 12:24:48 -0800498 private synchronized Collection<OFFlowStatsEntry> publishFlowStats(Dpid dpid,
499 OFFlowStatsReply reply) {
alshabib64def642014-12-02 23:27:37 -0800500 //TODO: Get rid of synchronized
sangho6a0bb172015-02-05 12:24:48 -0800501 fullFlowStats.putAll(dpid, reply.getEntries());
alshabib64def642014-12-02 23:27:37 -0800502 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
sangho6a0bb172015-02-05 12:24:48 -0800503 return fullFlowStats.removeAll(dpid);
504 }
505 return null;
506 }
507
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700508 private synchronized Collection<OFTableStatsEntry> publishTableStats(Dpid dpid,
509 OFTableStatsReply reply) {
510 //TODO: Get rid of synchronized
511 fullTableStats.putAll(dpid, reply.getEntries());
512 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
513 return fullTableStats.removeAll(dpid);
514 }
515 return null;
516 }
517
sangho6a0bb172015-02-05 12:24:48 -0800518 private synchronized Collection<OFGroupStatsEntry> publishGroupStats(Dpid dpid,
519 OFGroupStatsReply reply) {
520 //TODO: Get rid of synchronized
521 fullGroupStats.putAll(dpid, reply.getEntries());
522 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
523 return fullGroupStats.removeAll(dpid);
524 }
525 return null;
526 }
527
528 private synchronized Collection<OFGroupDescStatsEntry> publishGroupDescStats(Dpid dpid,
529 OFGroupDescStatsReply reply) {
530 //TODO: Get rid of synchronized
531 fullGroupDescStats.putAll(dpid, reply.getEntries());
532 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
533 return fullGroupDescStats.removeAll(dpid);
alshabib64def642014-12-02 23:27:37 -0800534 }
535 return null;
536 }
537
sangho538108b2015-04-08 14:29:20 -0700538 private synchronized Collection<OFPortStatsEntry> publishPortStats(Dpid dpid,
539 OFPortStatsReply reply) {
540 fullPortStats.putAll(dpid, reply.getEntries());
541 if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
542 return fullPortStats.removeAll(dpid);
543 }
544 return null;
545 }
546
tom7ef8ff92014-09-17 13:08:06 -0700547 @Override
548 public void setRole(Dpid dpid, RoleState role) {
Yuta HIGUCHI79cbd1c2014-10-02 16:57:57 -0700549 final OpenFlowSwitch sw = getSwitch(dpid);
550 if (sw == null) {
551 log.debug("Switch not connected. Ignoring setRole({}, {})", dpid, role);
552 return;
553 }
554 sw.setRole(role);
tom7ef8ff92014-09-17 13:08:06 -0700555 }
556
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700557 class InternalDeviceListener implements DeviceListener {
558
559 @Override
560 public boolean isRelevant(DeviceEvent event) {
Hyunsun Moone71494d2016-11-15 14:45:25 -0800561 return event.subject().type() != CONTROLLER && event.type() == DEVICE_REMOVED;
Yuta HIGUCHIcedd0df2016-08-23 11:44:13 -0700562 }
563
564 @Override
565 public void event(DeviceEvent event) {
566 switch (event.type()) {
567 case DEVICE_ADDED:
568 break;
569 case DEVICE_AVAILABILITY_CHANGED:
570 break;
571 case DEVICE_REMOVED:
572 // Device administratively removed, disconnect
573 Optional.ofNullable(getSwitch(dpid(event.subject().id().uri())))
574 .ifPresent(OpenFlowSwitch::disconnectSwitch);
575 break;
576 case DEVICE_SUSPENDED:
577 break;
578 case DEVICE_UPDATED:
579 break;
580 case PORT_ADDED:
581 break;
582 case PORT_REMOVED:
583 break;
584 case PORT_STATS_UPDATED:
585 break;
586 case PORT_UPDATED:
587 break;
588 default:
589 break;
590
591 }
592
593 }
594
595 }
596
tom7ef8ff92014-09-17 13:08:06 -0700597 /**
598 * Implementation of an OpenFlow Agent which is responsible for
599 * keeping track of connected switches and the state in which
600 * they are.
601 */
602 public class OpenFlowSwitchAgent implements OpenFlowAgent {
603
604 private final Logger log = LoggerFactory.getLogger(OpenFlowSwitchAgent.class);
605 private final Lock switchLock = new ReentrantLock();
606
607 @Override
608 public boolean addConnectedSwitch(Dpid dpid, OpenFlowSwitch sw) {
alshabib9eab22f2014-10-20 17:17:31 -0700609
tom7ef8ff92014-09-17 13:08:06 -0700610 if (connectedSwitches.get(dpid) != null) {
611 log.error("Trying to add connectedSwitch but found a previous "
612 + "value for dpid: {}", dpid);
613 return false;
614 } else {
Yuta HIGUCHIeb3f30b2014-10-22 11:34:49 -0700615 log.info("Added switch {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700616 connectedSwitches.put(dpid, sw);
alshabib8f1cf4a2014-09-17 14:44:48 -0700617 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700618 l.switchAdded(dpid);
619 }
620 return true;
621 }
622 }
623
624 @Override
625 public boolean validActivation(Dpid dpid) {
626 if (connectedSwitches.get(dpid) == null) {
627 log.error("Trying to activate switch but is not in "
628 + "connected switches: dpid {}. Aborting ..",
629 dpid);
630 return false;
631 }
632 if (activeMasterSwitches.get(dpid) != null ||
633 activeEqualSwitches.get(dpid) != null) {
634 log.error("Trying to activate switch but it is already "
635 + "activated: dpid {}. Found in activeMaster: {} "
Ray Milkey6bc43c22015-11-06 13:22:38 -0800636 + "Found in activeEqual: {}. Aborting ..",
637 dpid,
638 (activeMasterSwitches.get(dpid) == null) ? 'N' : 'Y',
639 (activeEqualSwitches.get(dpid) == null) ? 'N' : 'Y');
tom7ef8ff92014-09-17 13:08:06 -0700640 return false;
641 }
642 return true;
643 }
644
645
646 @Override
647 public boolean addActivatedMasterSwitch(Dpid dpid, OpenFlowSwitch sw) {
648 switchLock.lock();
649 try {
650 if (!validActivation(dpid)) {
651 return false;
652 }
653 activeMasterSwitches.put(dpid, sw);
654 return true;
655 } finally {
656 switchLock.unlock();
657 }
658 }
659
660 @Override
661 public boolean addActivatedEqualSwitch(Dpid dpid, OpenFlowSwitch sw) {
662 switchLock.lock();
663 try {
664 if (!validActivation(dpid)) {
665 return false;
666 }
667 activeEqualSwitches.put(dpid, sw);
668 log.info("Added Activated EQUAL Switch {}", dpid);
669 return true;
670 } finally {
671 switchLock.unlock();
672 }
673 }
674
675 @Override
676 public void transitionToMasterSwitch(Dpid dpid) {
677 switchLock.lock();
678 try {
679 if (activeMasterSwitches.containsKey(dpid)) {
680 return;
681 }
682 OpenFlowSwitch sw = activeEqualSwitches.remove(dpid);
683 if (sw == null) {
684 sw = getSwitch(dpid);
685 if (sw == null) {
686 log.error("Transition to master called on sw {}, but switch "
687 + "was not found in controller-cache", dpid);
688 return;
689 }
690 }
691 log.info("Transitioned switch {} to MASTER", dpid);
692 activeMasterSwitches.put(dpid, sw);
693 } finally {
694 switchLock.unlock();
695 }
696 }
697
698
699 @Override
700 public void transitionToEqualSwitch(Dpid dpid) {
701 switchLock.lock();
702 try {
703 if (activeEqualSwitches.containsKey(dpid)) {
704 return;
705 }
706 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
707 if (sw == null) {
708 sw = getSwitch(dpid);
709 if (sw == null) {
710 log.error("Transition to equal called on sw {}, but switch "
711 + "was not found in controller-cache", dpid);
712 return;
713 }
714 }
715 log.info("Transitioned switch {} to EQUAL", dpid);
716 activeEqualSwitches.put(dpid, sw);
717 } finally {
718 switchLock.unlock();
719 }
720
721 }
722
723 @Override
724 public void removeConnectedSwitch(Dpid dpid) {
725 connectedSwitches.remove(dpid);
726 OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
727 if (sw == null) {
Thomas Vachuska3358af22015-05-19 18:40:34 -0700728 log.debug("sw was null for {}", dpid);
tom7ef8ff92014-09-17 13:08:06 -0700729 sw = activeEqualSwitches.remove(dpid);
730 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700731 for (OpenFlowSwitchListener l : ofSwitchListener) {
tom7ef8ff92014-09-17 13:08:06 -0700732 l.switchRemoved(dpid);
733 }
734 }
735
736 @Override
Jian Lia78cdb22016-04-21 13:03:58 -0700737 public void processDownstreamMessage(Dpid dpid, List<OFMessage> m) {
738 for (OpenFlowMessageListener listener : ofMessageListener) {
739 listener.handleOutgoingMessage(dpid, m);
740 }
741 }
742
743
744 @Override
tom7ef8ff92014-09-17 13:08:06 -0700745 public void processMessage(Dpid dpid, OFMessage m) {
746 processPacket(dpid, m);
Jian Lia78cdb22016-04-21 13:03:58 -0700747
748 for (OpenFlowMessageListener listener : ofMessageListener) {
749 listener.handleIncomingMessage(dpid, m);
750 }
tom7ef8ff92014-09-17 13:08:06 -0700751 }
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700752
753 @Override
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700754 public void returnRoleReply(Dpid dpid, RoleState requested, RoleState response) {
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700755 for (OpenFlowSwitchListener l : ofSwitchListener) {
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700756 l.receivedRoleReply(dpid, requested, response);
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700757 }
758 }
tom7ef8ff92014-09-17 13:08:06 -0700759 }
760
Jian Li152b8852015-12-07 14:47:25 -0800761 /**
Jian Li2266bff2016-04-21 11:01:25 -0700762 * OpenFlow message handler.
Jian Li152b8852015-12-07 14:47:25 -0800763 */
Ray Milkey7ec0d1b2015-11-13 08:51:35 -0800764 protected final class OFMessageHandler implements Runnable {
alshabib8f1cf4a2014-09-17 14:44:48 -0700765
Ray Milkey7ec0d1b2015-11-13 08:51:35 -0800766 protected final OFMessage msg;
767 protected final Dpid dpid;
alshabib8f1cf4a2014-09-17 14:44:48 -0700768
769 public OFMessageHandler(Dpid dpid, OFMessage msg) {
770 this.msg = msg;
771 this.dpid = dpid;
772 }
773
774 @Override
775 public void run() {
alshabibeec3a062014-09-17 18:01:26 -0700776 for (OpenFlowEventListener listener : ofEventListener) {
alshabib8f1cf4a2014-09-17 14:44:48 -0700777 listener.handleMessage(dpid, msg);
778 }
779 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700780 }
tom7ef8ff92014-09-17 13:08:06 -0700781}