blob: d04af5119e00d974472510393f5174e4ff638245 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
jcc3d4e14a2015-04-21 11:32:05 +08002 * 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 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.provider.of.flow.impl;
alshabib1cc04f72014-09-16 16:09:58 -070017
jcc3d4e14a2015-04-21 11:32:05 +080018import static org.slf4j.LoggerFactory.getLogger;
Jonathan Hart2ffcd102015-01-16 16:47:50 -080019
jcc3d4e14a2015-04-21 11:32:05 +080020import java.util.Collections;
21import java.util.List;
22import java.util.Map;
23import java.util.Optional;
24import java.util.Set;
25import java.util.concurrent.TimeUnit;
26import java.util.stream.Collectors;
27
alshabib1cc04f72014-09-16 16:09:58 -070028import 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.Reference;
32import org.apache.felix.scr.annotations.ReferenceCardinality;
Brian O'Connorabafb502014-12-02 22:26:20 -080033import org.onosproject.core.ApplicationId;
34import org.onosproject.net.DeviceId;
Brian O'Connorabafb502014-12-02 22:26:20 -080035import org.onosproject.net.flow.CompletedBatchOperation;
Brian O'Connorabafb502014-12-02 22:26:20 -080036import org.onosproject.net.flow.FlowEntry;
37import org.onosproject.net.flow.FlowRule;
38import org.onosproject.net.flow.FlowRuleBatchEntry;
Brian O'Connor72cb19a2015-01-16 16:14:41 -080039import org.onosproject.net.flow.FlowRuleBatchOperation;
Brian O'Connorabafb502014-12-02 22:26:20 -080040import org.onosproject.net.flow.FlowRuleProvider;
41import org.onosproject.net.flow.FlowRuleProviderRegistry;
42import org.onosproject.net.flow.FlowRuleProviderService;
43import org.onosproject.net.provider.AbstractProvider;
44import org.onosproject.net.provider.ProviderId;
Brian O'Connorabafb502014-12-02 22:26:20 -080045import org.onosproject.openflow.controller.Dpid;
46import org.onosproject.openflow.controller.OpenFlowController;
47import org.onosproject.openflow.controller.OpenFlowEventListener;
48import org.onosproject.openflow.controller.OpenFlowSwitch;
49import org.onosproject.openflow.controller.OpenFlowSwitchListener;
50import org.onosproject.openflow.controller.RoleState;
jcc3d4e14a2015-04-21 11:32:05 +080051import org.onosproject.openflow.controller.ThirdPartyMessage;
alshabib902d41b2014-10-07 16:52:05 -070052import org.projectfloodlight.openflow.protocol.OFBarrierRequest;
53import org.projectfloodlight.openflow.protocol.OFErrorMsg;
Brian O'Connor72cb19a2015-01-16 16:14:41 -080054import org.projectfloodlight.openflow.protocol.OFErrorType;
alshabib193525b2014-10-08 18:58:03 -070055import org.projectfloodlight.openflow.protocol.OFFlowMod;
alshabib8f1cf4a2014-09-17 14:44:48 -070056import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
alshabib5c370ff2014-09-18 10:12:14 -070057import org.projectfloodlight.openflow.protocol.OFFlowStatsReply;
alshabib8f1cf4a2014-09-17 14:44:48 -070058import org.projectfloodlight.openflow.protocol.OFMessage;
59import org.projectfloodlight.openflow.protocol.OFPortStatus;
alshabib5c370ff2014-09-18 10:12:14 -070060import org.projectfloodlight.openflow.protocol.OFStatsReply;
sangho89bf6fb2015-02-09 09:33:13 -080061import org.projectfloodlight.openflow.protocol.OFStatsType;
alshabib193525b2014-10-08 18:58:03 -070062import org.projectfloodlight.openflow.protocol.errormsg.OFFlowModFailedErrorMsg;
alshabib1cc04f72014-09-16 16:09:58 -070063import org.slf4j.Logger;
64
jcc3d4e14a2015-04-21 11:32:05 +080065import com.google.common.cache.Cache;
66import com.google.common.cache.CacheBuilder;
67import com.google.common.cache.RemovalCause;
68import com.google.common.cache.RemovalNotification;
69import com.google.common.collect.Maps;
70import com.google.common.collect.Sets;
alshabibeec3a062014-09-17 18:01:26 -070071
alshabib1cc04f72014-09-16 16:09:58 -070072/**
jcc3d4e14a2015-04-21 11:32:05 +080073 * Provider which uses an OpenFlow controller to detect network end-station
74 * hosts.
alshabib1cc04f72014-09-16 16:09:58 -070075 */
76@Component(immediate = true)
jcc3d4e14a2015-04-21 11:32:05 +080077public class OpenFlowRuleProvider extends AbstractProvider
78 implements FlowRuleProvider {
alshabib1cc04f72014-09-16 16:09:58 -070079
80 private final Logger log = getLogger(getClass());
81
82 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
83 protected FlowRuleProviderRegistry providerRegistry;
84
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 protected OpenFlowController controller;
87
alshabib1cc04f72014-09-16 16:09:58 -070088 private FlowRuleProviderService providerService;
89
alshabibeec3a062014-09-17 18:01:26 -070090 private final InternalFlowProvider listener = new InternalFlowProvider();
91
Brian O'Connor72cb19a2015-01-16 16:14:41 -080092 private Cache<Long, InternalCacheEntry> pendingBatches;
alshabib193525b2014-10-08 18:58:03 -070093
alshabib3d643ec2014-10-22 18:33:00 -070094 private final Map<Dpid, FlowStatsCollector> collectors = Maps.newHashMap();
95
alshabib1cc04f72014-09-16 16:09:58 -070096 /**
97 * Creates an OpenFlow host provider.
98 */
99 public OpenFlowRuleProvider() {
Brian O'Connorabafb502014-12-02 22:26:20 -0800100 super(new ProviderId("of", "org.onosproject.provider.openflow"));
alshabib1cc04f72014-09-16 16:09:58 -0700101 }
102
103 @Activate
104 public void activate() {
105 providerService = providerRegistry.register(this);
alshabibeec3a062014-09-17 18:01:26 -0700106 controller.addListener(listener);
107 controller.addEventListener(listener);
alshabib3d643ec2014-10-22 18:33:00 -0700108
jcc3d4e14a2015-04-21 11:32:05 +0800109 pendingBatches = CacheBuilder
110 .newBuilder()
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800111 .expireAfterWrite(10, TimeUnit.SECONDS)
112 .removalListener((RemovalNotification<Long, InternalCacheEntry> notification) -> {
jcc3d4e14a2015-04-21 11:32:05 +0800113 if (notification.getCause() == RemovalCause.EXPIRED) {
114 providerService
115 .batchOperationCompleted(notification
116 .getKey(),
117 notification
118 .getValue()
119 .failedCompletion());
120 }
121 }).build();
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800122
alshabib3d643ec2014-10-22 18:33:00 -0700123 for (OpenFlowSwitch sw : controller.getSwitches()) {
124 FlowStatsCollector fsc = new FlowStatsCollector(sw, POLL_INTERVAL);
125 fsc.start();
126 collectors.put(new Dpid(sw.getId()), fsc);
127 }
128
alshabib1cc04f72014-09-16 16:09:58 -0700129 log.info("Started");
130 }
131
132 @Deactivate
133 public void deactivate() {
134 providerRegistry.unregister(this);
135 providerService = null;
136
137 log.info("Stopped");
138 }
Thomas Vachuska9b2da212014-11-10 19:30:25 -0800139
alshabib1cc04f72014-09-16 16:09:58 -0700140 @Override
141 public void applyFlowRule(FlowRule... flowRules) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800142 for (FlowRule flowRule : flowRules) {
143 applyRule(flowRule);
alshabib35edb1a2014-09-16 17:44:44 -0700144 }
alshabib1cc04f72014-09-16 16:09:58 -0700145 }
146
alshabib35edb1a2014-09-16 17:44:44 -0700147 private void applyRule(FlowRule flowRule) {
jcc3d4e14a2015-04-21 11:32:05 +0800148 OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId()
149 .uri()));
150 if (flowRule.payLoad().payLoad().length > 0) {
151 OFMessage msg = new ThirdPartyMessage(flowRule.payLoad().payLoad());
152 sw.sendMsg(msg);
153 return;
154 }
alshabibbdcbb102015-04-22 14:16:38 -0700155 sw.sendMsg(FlowModBuilder.builder(flowRule, sw.factory(),
jcc3d4e14a2015-04-21 11:32:05 +0800156 Optional.empty()).buildFlowAdd());
alshabib35edb1a2014-09-16 17:44:44 -0700157 }
158
alshabib1cc04f72014-09-16 16:09:58 -0700159 @Override
160 public void removeFlowRule(FlowRule... flowRules) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800161 for (FlowRule flowRule : flowRules) {
162 removeRule(flowRule);
alshabib219ebaa2014-09-22 15:41:24 -0700163 }
alshabib1cc04f72014-09-16 16:09:58 -0700164
165 }
166
alshabib219ebaa2014-09-22 15:41:24 -0700167 private void removeRule(FlowRule flowRule) {
jcc3d4e14a2015-04-21 11:32:05 +0800168 OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId()
169 .uri()));
170 if (flowRule.payLoad().payLoad().length > 0) {
171 OFMessage msg = new ThirdPartyMessage(flowRule.payLoad().payLoad());
172 sw.sendMsg(msg);
173 return;
174 }
alshabibbdcbb102015-04-22 14:16:38 -0700175 sw.sendMsg(FlowModBuilder.builder(flowRule, sw.factory(),
jcc3d4e14a2015-04-21 11:32:05 +0800176 Optional.empty()).buildFlowDel());
alshabib219ebaa2014-09-22 15:41:24 -0700177 }
178
alshabiba68eb962014-09-24 20:34:13 -0700179 @Override
180 public void removeRulesById(ApplicationId id, FlowRule... flowRules) {
181 // TODO: optimize using the ApplicationId
182 removeFlowRule(flowRules);
183 }
184
alshabib193525b2014-10-08 18:58:03 -0700185 @Override
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800186 public void executeBatch(FlowRuleBatchOperation batch) {
187
188 pendingBatches.put(batch.id(), new InternalCacheEntry(batch));
189
jcc3d4e14a2015-04-21 11:32:05 +0800190 OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(batch.deviceId()
191 .uri()));
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800192 OFFlowMod mod;
alshabib193525b2014-10-08 18:58:03 -0700193 for (FlowRuleBatchEntry fbe : batch.getOperations()) {
jcc3d4e14a2015-04-21 11:32:05 +0800194 // flow is the third party privacy flow
195 if (fbe.target().payLoad().payLoad().length > 0) {
196 OFMessage msg = new ThirdPartyMessage(fbe.target().payLoad()
197 .payLoad());
198 sw.sendMsg(msg);
199 continue;
200 }
201 FlowModBuilder builder = FlowModBuilder.builder(fbe.target(), sw
202 .factory(), Optional.of(batch.id()));
Sho SHIMIZUaba9d002015-01-29 14:51:04 -0800203 switch (fbe.operator()) {
jcc3d4e14a2015-04-21 11:32:05 +0800204 case ADD:
205 mod = builder.buildFlowAdd();
206 break;
207 case REMOVE:
208 mod = builder.buildFlowDel();
209 break;
210 case MODIFY:
211 mod = builder.buildFlowMod();
212 break;
213 default:
214 log.error("Unsupported batch operation {}; skipping flowmod {}",
215 fbe.operator(), fbe);
216 continue;
217 }
Saurav Das3ea46622015-04-22 14:01:34 -0700218 sw.sendMsg(mod);
alshabib193525b2014-10-08 18:58:03 -0700219 }
jcc3d4e14a2015-04-21 11:32:05 +0800220 OFBarrierRequest.Builder builder = sw.factory().buildBarrierRequest()
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800221 .setXid(batch.id());
222 sw.sendMsg(builder.build());
alshabib193525b2014-10-08 18:58:03 -0700223 }
224
alshabib8f1cf4a2014-09-17 14:44:48 -0700225 private class InternalFlowProvider
Thomas Vachuska9b2da212014-11-10 19:30:25 -0800226 implements OpenFlowSwitchListener, OpenFlowEventListener {
alshabib8f1cf4a2014-09-17 14:44:48 -0700227
alshabib8f1cf4a2014-09-17 14:44:48 -0700228 @Override
229 public void switchAdded(Dpid dpid) {
jcc3d4e14a2015-04-21 11:32:05 +0800230 FlowStatsCollector fsc = new FlowStatsCollector(
231 controller
232 .getSwitch(dpid),
233 POLL_INTERVAL);
alshabibeec3a062014-09-17 18:01:26 -0700234 fsc.start();
235 collectors.put(dpid, fsc);
alshabib8f1cf4a2014-09-17 14:44:48 -0700236 }
237
238 @Override
239 public void switchRemoved(Dpid dpid) {
alshabibdfc7afb2014-10-21 20:13:27 -0700240 FlowStatsCollector collector = collectors.remove(dpid);
241 if (collector != null) {
242 collector.stop();
243 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700244 }
245
246 @Override
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700247 public void switchChanged(Dpid dpid) {
248 }
249
250 @Override
alshabib8f1cf4a2014-09-17 14:44:48 -0700251 public void portChanged(Dpid dpid, OFPortStatus status) {
jcc3d4e14a2015-04-21 11:32:05 +0800252 // TODO: Decide whether to evict flows internal store.
alshabib8f1cf4a2014-09-17 14:44:48 -0700253 }
254
255 @Override
256 public void handleMessage(Dpid dpid, OFMessage msg) {
alshabibda1644e2015-03-13 14:01:35 -0700257 OpenFlowSwitch sw = controller.getSwitch(dpid);
alshabib8f1cf4a2014-09-17 14:44:48 -0700258 switch (msg.getType()) {
jcc3d4e14a2015-04-21 11:32:05 +0800259 case FLOW_REMOVED:
260 OFFlowRemoved removed = (OFFlowRemoved) msg;
alshabib6b5cfec2014-09-18 17:42:18 -0700261
jcc3d4e14a2015-04-21 11:32:05 +0800262 FlowEntry fr = new FlowEntryBuilder(dpid, removed).build();
263 providerService.flowRemoved(fr);
264 break;
265 case STATS_REPLY:
266 if (((OFStatsReply) msg).getStatsType() == OFStatsType.FLOW) {
267 pushFlowMetrics(dpid, (OFFlowStatsReply) msg);
268 }
269 break;
270 case BARRIER_REPLY:
271 try {
272 InternalCacheEntry entry = pendingBatches.getIfPresent(msg
273 .getXid());
274 if (entry != null) {
275 providerService
276 .batchOperationCompleted(msg.getXid(),
277 entry.completed());
278 } else {
279 log.warn("Received unknown Barrier Reply: {}",
280 msg.getXid());
sangho89bf6fb2015-02-09 09:33:13 -0800281 }
jcc3d4e14a2015-04-21 11:32:05 +0800282 } finally {
283 pendingBatches.invalidate(msg.getXid());
284 }
285 break;
286 case ERROR:
287 log.warn("received Error message {} from {}", msg, dpid);
288
289 OFErrorMsg error = (OFErrorMsg) msg;
290 if (error.getErrType() == OFErrorType.FLOW_MOD_FAILED) {
291 OFFlowModFailedErrorMsg fmFailed = (OFFlowModFailedErrorMsg) error;
292 if (fmFailed.getData().getParsedMessage().isPresent()) {
293 OFMessage m = fmFailed.getData().getParsedMessage()
294 .get();
295 OFFlowMod fm = (OFFlowMod) m;
296 InternalCacheEntry entry = pendingBatches
297 .getIfPresent(msg.getXid());
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800298 if (entry != null) {
jcc3d4e14a2015-04-21 11:32:05 +0800299 entry.appendFailure(new FlowEntryBuilder(dpid, fm)
300 .build());
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800301 } else {
jcc3d4e14a2015-04-21 11:32:05 +0800302 log.error("No matching batch for this error: {}",
303 error);
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800304 }
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800305 } else {
jcc3d4e14a2015-04-21 11:32:05 +0800306 // FIXME: Potentially add flowtracking to avoid this
307 // message.
308 log.error("Flow installation failed but switch didn't"
309 + " tell us which one.");
Thomas Vachuska9b2da212014-11-10 19:30:25 -0800310 }
jcc3d4e14a2015-04-21 11:32:05 +0800311 } else {
312 log.warn("Received error {}", error);
313 }
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800314
jcc3d4e14a2015-04-21 11:32:05 +0800315 default:
316 log.debug("Unhandled message type: {}", msg.getType());
alshabib8f1cf4a2014-09-17 14:44:48 -0700317 }
318
319 }
320
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700321 @Override
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700322 public void receivedRoleReply(Dpid dpid, RoleState requested,
Thomas Vachuska9b2da212014-11-10 19:30:25 -0800323 RoleState response) {
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700324 // Do nothing here for now.
325 }
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700326
sangho89bf6fb2015-02-09 09:33:13 -0800327 private void pushFlowMetrics(Dpid dpid, OFFlowStatsReply replies) {
alshabib64def642014-12-02 23:27:37 -0800328
alshabib54ce5892014-09-23 17:50:51 -0700329 DeviceId did = DeviceId.deviceId(Dpid.uri(dpid));
Saurav Dasfa2fa932015-03-03 11:29:48 -0800330 OpenFlowSwitch sw = controller.getSwitch(dpid);
alshabib54ce5892014-09-23 17:50:51 -0700331
alshabib64def642014-12-02 23:27:37 -0800332 List<FlowEntry> flowEntries = replies.getEntries().stream()
alshabibbdcbb102015-04-22 14:16:38 -0700333 .map(entry -> new FlowEntryBuilder(dpid, entry).build())
alshabib64def642014-12-02 23:27:37 -0800334 .collect(Collectors.toList());
alshabib54ce5892014-09-23 17:50:51 -0700335
alshabib64def642014-12-02 23:27:37 -0800336 providerService.pushFlowMetrics(did, flowEntries);
337
alshabib5c370ff2014-09-18 10:12:14 -0700338 }
339
alshabib8f1cf4a2014-09-17 14:44:48 -0700340 }
alshabib1cc04f72014-09-16 16:09:58 -0700341
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800342 /**
jcc3d4e14a2015-04-21 11:32:05 +0800343 * The internal cache entry holding the original request as well as
344 * accumulating the any failures along the way.
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800345 *
jcc3d4e14a2015-04-21 11:32:05 +0800346 * If this entry is evicted from the cache then the entire operation is
347 * considered failed. Otherwise, only the failures reported by the device
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800348 * will be propagated up.
349 */
350 private class InternalCacheEntry {
alshabib902d41b2014-10-07 16:52:05 -0700351
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800352 private final FlowRuleBatchOperation operation;
353 private final Set<FlowRule> failures = Sets.newConcurrentHashSet();
alshabib193525b2014-10-08 18:58:03 -0700354
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800355 public InternalCacheEntry(FlowRuleBatchOperation operation) {
356 this.operation = operation;
alshabib902d41b2014-10-07 16:52:05 -0700357 }
358
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800359 /**
360 * Appends a failed rule to the set of failed items.
jcc3d4e14a2015-04-21 11:32:05 +0800361 *
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800362 * @param rule the failed rule
363 */
364 public void appendFailure(FlowRule rule) {
365 failures.add(rule);
Thomas Vachuska9b2da212014-11-10 19:30:25 -0800366 }
367
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800368 /**
369 * Fails the entire batch and returns the failed operation.
jcc3d4e14a2015-04-21 11:32:05 +0800370 *
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800371 * @return the failed operation
372 */
373 public CompletedBatchOperation failedCompletion() {
374 Set<FlowRule> fails = operation.getOperations().stream()
375 .map(op -> op.target()).collect(Collectors.toSet());
jcc3d4e14a2015-04-21 11:32:05 +0800376 return new CompletedBatchOperation(false,
377 Collections
378 .unmodifiableSet(fails),
379 operation.deviceId());
alshabib902d41b2014-10-07 16:52:05 -0700380 }
381
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800382 /**
383 * Returns the completed operation and whether the batch suceeded.
jcc3d4e14a2015-04-21 11:32:05 +0800384 *
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800385 * @return the completed operation
386 */
387 public CompletedBatchOperation completed() {
jcc3d4e14a2015-04-21 11:32:05 +0800388 return new CompletedBatchOperation(
389 failures.isEmpty(),
390 Collections
391 .unmodifiableSet(failures),
392 operation.deviceId());
alshabib902d41b2014-10-07 16:52:05 -0700393 }
394
alshabib902d41b2014-10-07 16:52:05 -0700395 }
alshabiba68eb962014-09-24 20:34:13 -0700396
alshabib1cc04f72014-09-16 16:09:58 -0700397}