blob: c6d076913b89faa26b5a5a64c75bb38c263f458c [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2014-present Open Networking Foundation
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
Thomas Vachuska75aaa672015-04-29 12:24:43 -070018import com.google.common.cache.Cache;
19import com.google.common.cache.CacheBuilder;
20import com.google.common.cache.RemovalCause;
21import com.google.common.cache.RemovalNotification;
Madan Jampani84382b92016-06-22 08:26:49 -070022import com.google.common.collect.ImmutableSet;
23import com.google.common.collect.Lists;
Thomas Vachuska75aaa672015-04-29 12:24:43 -070024import com.google.common.collect.Maps;
25import com.google.common.collect.Sets;
Madan Jampani84382b92016-06-22 08:26:49 -070026
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070027import io.netty.buffer.ByteBuf;
28import io.netty.buffer.Unpooled;
alshabib1cc04f72014-09-16 16:09:58 -070029import org.apache.felix.scr.annotations.Activate;
30import org.apache.felix.scr.annotations.Component;
31import org.apache.felix.scr.annotations.Deactivate;
Thomas Vachuska75aaa672015-04-29 12:24:43 -070032import org.apache.felix.scr.annotations.Modified;
33import org.apache.felix.scr.annotations.Property;
alshabib1cc04f72014-09-16 16:09:58 -070034import org.apache.felix.scr.annotations.Reference;
35import org.apache.felix.scr.annotations.ReferenceCardinality;
Thomas Vachuska75aaa672015-04-29 12:24:43 -070036import org.onosproject.cfg.ComponentConfigService;
Brian O'Connorabafb502014-12-02 22:26:20 -080037import org.onosproject.core.ApplicationId;
38import org.onosproject.net.DeviceId;
Rodrigo Duarte Sousae37d1292017-08-29 17:01:24 -030039import org.onosproject.net.driver.DefaultDriverData;
40import org.onosproject.net.driver.DefaultDriverHandler;
41import org.onosproject.net.driver.Driver;
42import org.onosproject.net.driver.DriverHandler;
Jonathan Hart3c259162015-10-21 21:31:19 -070043import org.onosproject.net.driver.DriverService;
Brian O'Connorabafb502014-12-02 22:26:20 -080044import org.onosproject.net.flow.CompletedBatchOperation;
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -070045import org.onosproject.net.flow.DefaultTableStatisticsEntry;
Brian O'Connorabafb502014-12-02 22:26:20 -080046import org.onosproject.net.flow.FlowEntry;
47import org.onosproject.net.flow.FlowRule;
Ray Milkey7bf273c2017-09-27 16:15:15 -070048import org.onosproject.net.flow.oldbatch.FlowRuleBatchEntry;
49import org.onosproject.net.flow.oldbatch.FlowRuleBatchOperation;
Thomas Vachuskaa6c0d042015-04-23 10:17:37 -070050import org.onosproject.net.flow.FlowRuleExtPayLoad;
Brian O'Connorabafb502014-12-02 22:26:20 -080051import org.onosproject.net.flow.FlowRuleProvider;
52import org.onosproject.net.flow.FlowRuleProviderRegistry;
53import org.onosproject.net.flow.FlowRuleProviderService;
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -070054import org.onosproject.net.flow.TableStatisticsEntry;
hjtsao1a4333c2018-10-22 11:02:00 -070055import org.onosproject.net.flow.IndexTableId;
Brian O'Connorabafb502014-12-02 22:26:20 -080056import org.onosproject.net.provider.AbstractProvider;
57import org.onosproject.net.provider.ProviderId;
Thomas Vachuska75aaa672015-04-29 12:24:43 -070058import org.onosproject.net.statistic.DefaultLoad;
Brian O'Connorabafb502014-12-02 22:26:20 -080059import org.onosproject.openflow.controller.Dpid;
60import org.onosproject.openflow.controller.OpenFlowController;
61import org.onosproject.openflow.controller.OpenFlowEventListener;
62import org.onosproject.openflow.controller.OpenFlowSwitch;
63import org.onosproject.openflow.controller.OpenFlowSwitchListener;
64import org.onosproject.openflow.controller.RoleState;
jcc3d4e14a2015-04-21 11:32:05 +080065import org.onosproject.openflow.controller.ThirdPartyMessage;
Thomas Vachuska95caba32016-04-04 10:42:05 -070066import org.onosproject.provider.of.flow.util.FlowEntryBuilder;
Thomas Vachuska75aaa672015-04-29 12:24:43 -070067import org.osgi.service.component.ComponentContext;
Thomas Vachuska3358af22015-05-19 18:40:34 -070068import org.projectfloodlight.openflow.protocol.OFBadRequestCode;
alshabib902d41b2014-10-07 16:52:05 -070069import org.projectfloodlight.openflow.protocol.OFBarrierRequest;
Laszlo Pappedadbe22017-12-14 20:05:49 +000070import org.projectfloodlight.openflow.protocol.OFCapabilities;
alshabib902d41b2014-10-07 16:52:05 -070071import org.projectfloodlight.openflow.protocol.OFErrorMsg;
Cem Türker3baff672017-10-12 15:09:01 +030072import org.projectfloodlight.openflow.protocol.OFFlowLightweightStatsReply;
alshabib193525b2014-10-08 18:58:03 -070073import org.projectfloodlight.openflow.protocol.OFFlowMod;
alshabib8f1cf4a2014-09-17 14:44:48 -070074import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
alshabib5c370ff2014-09-18 10:12:14 -070075import org.projectfloodlight.openflow.protocol.OFFlowStatsReply;
alshabib8f1cf4a2014-09-17 14:44:48 -070076import org.projectfloodlight.openflow.protocol.OFMessage;
77import org.projectfloodlight.openflow.protocol.OFPortStatus;
alshabib5c370ff2014-09-18 10:12:14 -070078import org.projectfloodlight.openflow.protocol.OFStatsReply;
sangho89bf6fb2015-02-09 09:33:13 -080079import org.projectfloodlight.openflow.protocol.OFStatsType;
Jonathan Hart3c259162015-10-21 21:31:19 -070080import org.projectfloodlight.openflow.protocol.OFTableStatsEntry;
81import org.projectfloodlight.openflow.protocol.OFTableStatsReply;
Prince Pereira141ed812016-09-02 19:03:18 +053082import org.projectfloodlight.openflow.protocol.OFType;
83import org.projectfloodlight.openflow.protocol.OFVersion;
Prince Pereira788797e2016-08-10 11:24:14 +053084import org.projectfloodlight.openflow.protocol.errormsg.OFBadActionErrorMsg;
85import org.projectfloodlight.openflow.protocol.errormsg.OFBadInstructionErrorMsg;
86import org.projectfloodlight.openflow.protocol.errormsg.OFBadMatchErrorMsg;
Thomas Vachuska3358af22015-05-19 18:40:34 -070087import org.projectfloodlight.openflow.protocol.errormsg.OFBadRequestErrorMsg;
alshabib193525b2014-10-08 18:58:03 -070088import org.projectfloodlight.openflow.protocol.errormsg.OFFlowModFailedErrorMsg;
Prince Pereira141ed812016-09-02 19:03:18 +053089import org.projectfloodlight.openflow.types.U16;
90import org.projectfloodlight.openflow.types.U64;
alshabib1cc04f72014-09-16 16:09:58 -070091import org.slf4j.Logger;
92
Thomas Vachuska75aaa672015-04-29 12:24:43 -070093import java.util.Collections;
94import java.util.Dictionary;
95import java.util.List;
96import java.util.Map;
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -070097import java.util.Objects;
Thomas Vachuska75aaa672015-04-29 12:24:43 -070098import java.util.Optional;
99import java.util.Set;
100import java.util.Timer;
101import java.util.concurrent.TimeUnit;
102import java.util.stream.Collectors;
103
ssyoon9030fbcd92015-08-17 10:42:07 +0900104import static com.google.common.base.Preconditions.checkNotNull;
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700105import static com.google.common.base.Strings.isNullOrEmpty;
106import static org.onlab.util.Tools.get;
107import static org.slf4j.LoggerFactory.getLogger;
alshabibeec3a062014-09-17 18:01:26 -0700108
alshabib1cc04f72014-09-16 16:09:58 -0700109/**
jcc3d4e14a2015-04-21 11:32:05 +0800110 * Provider which uses an OpenFlow controller to detect network end-station
111 * hosts.
alshabib1cc04f72014-09-16 16:09:58 -0700112 */
113@Component(immediate = true)
jcc3d4e14a2015-04-21 11:32:05 +0800114public class OpenFlowRuleProvider extends AbstractProvider
115 implements FlowRuleProvider {
alshabib1cc04f72014-09-16 16:09:58 -0700116
117 private final Logger log = getLogger(getClass());
118
119 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
120 protected FlowRuleProviderRegistry providerRegistry;
121
122 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
123 protected OpenFlowController controller;
124
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700125 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
126 protected ComponentConfigService cfgService;
127
Jonathan Hart3c259162015-10-21 21:31:19 -0700128 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
129 protected DriverService driverService;
130
ssyoon9030fbcd92015-08-17 10:42:07 +0900131 private static final int DEFAULT_POLL_FREQUENCY = 5;
Prince Pereira141ed812016-09-02 19:03:18 +0530132 private static final int MIN_EXPECTED_BYTE_LEN = 56;
133 private static final int SKIP_BYTES = 4;
134 private static final boolean DEFAULT_ADAPTIVE_FLOW_SAMPLING = false;
135
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700136 @Property(name = "flowPollFrequency", intValue = DEFAULT_POLL_FREQUENCY,
137 label = "Frequency (in seconds) for polling flow statistics")
138 private int flowPollFrequency = DEFAULT_POLL_FREQUENCY;
139
ssyoon9030fbcd92015-08-17 10:42:07 +0900140 @Property(name = "adaptiveFlowSampling", boolValue = DEFAULT_ADAPTIVE_FLOW_SAMPLING,
141 label = "Adaptive Flow Sampling is on or off")
142 private boolean adaptiveFlowSampling = DEFAULT_ADAPTIVE_FLOW_SAMPLING;
143
alshabib1cc04f72014-09-16 16:09:58 -0700144 private FlowRuleProviderService providerService;
145
alshabibeec3a062014-09-17 18:01:26 -0700146 private final InternalFlowProvider listener = new InternalFlowProvider();
147
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800148 private Cache<Long, InternalCacheEntry> pendingBatches;
alshabib193525b2014-10-08 18:58:03 -0700149
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900150 private final Timer timer = new Timer("onos-openflow-collector");
151
Cem Türker3baff672017-10-12 15:09:01 +0300152
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900153 // Old simple collector set
Madan Jampani6b266102016-06-23 00:56:36 -0700154 private final Map<Dpid, FlowStatsCollector> simpleCollectors = Maps.newConcurrentMap();
ssyoon9030fbcd92015-08-17 10:42:07 +0900155
156 // NewAdaptiveFlowStatsCollector Set
Madan Jampani6b266102016-06-23 00:56:36 -0700157 private final Map<Dpid, NewAdaptiveFlowStatsCollector> afsCollectors = Maps.newConcurrentMap();
158 private final Map<Dpid, TableStatisticsCollector> tableStatsCollectors = Maps.newConcurrentMap();
alshabib3d643ec2014-10-22 18:33:00 -0700159
alshabib1cc04f72014-09-16 16:09:58 -0700160 /**
161 * Creates an OpenFlow host provider.
162 */
163 public OpenFlowRuleProvider() {
Brian O'Connorabafb502014-12-02 22:26:20 -0800164 super(new ProviderId("of", "org.onosproject.provider.openflow"));
alshabib1cc04f72014-09-16 16:09:58 -0700165 }
166
167 @Activate
Thomas Vachuskaa394b952016-06-14 15:02:09 -0700168 protected void activate(ComponentContext context) {
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700169 cfgService.registerProperties(getClass());
alshabib1cc04f72014-09-16 16:09:58 -0700170 providerService = providerRegistry.register(this);
alshabibeec3a062014-09-17 18:01:26 -0700171 controller.addListener(listener);
172 controller.addEventListener(listener);
alshabib3d643ec2014-10-22 18:33:00 -0700173
Antonio Marsico1c5ae1f2015-12-15 15:31:56 +0100174 modified(context);
175
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700176 pendingBatches = createBatchCache();
ssyoon9030fbcd92015-08-17 10:42:07 +0900177
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700178 createCollectors();
alshabib3d643ec2014-10-22 18:33:00 -0700179
ssyoon9030fbcd92015-08-17 10:42:07 +0900180 log.info("Started with flowPollFrequency = {}, adaptiveFlowSampling = {}",
181 flowPollFrequency, adaptiveFlowSampling);
alshabib1cc04f72014-09-16 16:09:58 -0700182 }
183
184 @Deactivate
Thomas Vachuskaa394b952016-06-14 15:02:09 -0700185 protected void deactivate(ComponentContext context) {
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700186 cfgService.unregisterProperties(getClass(), false);
187 stopCollectors();
alshabib1cc04f72014-09-16 16:09:58 -0700188 providerRegistry.unregister(this);
189 providerService = null;
190
191 log.info("Stopped");
192 }
Thomas Vachuska9b2da212014-11-10 19:30:25 -0800193
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700194 @Modified
Thomas Vachuskaa394b952016-06-14 15:02:09 -0700195 protected void modified(ComponentContext context) {
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700196 Dictionary<?, ?> properties = context.getProperties();
197 int newFlowPollFrequency;
198 try {
199 String s = get(properties, "flowPollFrequency");
200 newFlowPollFrequency = isNullOrEmpty(s) ? flowPollFrequency : Integer.parseInt(s.trim());
201
202 } catch (NumberFormatException | ClassCastException e) {
203 newFlowPollFrequency = flowPollFrequency;
204 }
205
206 if (newFlowPollFrequency != flowPollFrequency) {
207 flowPollFrequency = newFlowPollFrequency;
208 adjustRate();
209 }
210
211 log.info("Settings: flowPollFrequency={}", flowPollFrequency);
ssyoon9030fbcd92015-08-17 10:42:07 +0900212
213 boolean newAdaptiveFlowSampling;
214 String s = get(properties, "adaptiveFlowSampling");
215 newAdaptiveFlowSampling = isNullOrEmpty(s) ? adaptiveFlowSampling : Boolean.parseBoolean(s.trim());
216
217 if (newAdaptiveFlowSampling != adaptiveFlowSampling) {
218 // stop previous collector
219 stopCollectors();
220 adaptiveFlowSampling = newAdaptiveFlowSampling;
221 // create new collectors
222 createCollectors();
223 }
224
225 log.info("Settings: adaptiveFlowSampling={}", adaptiveFlowSampling);
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700226 }
227
228 private Cache<Long, InternalCacheEntry> createBatchCache() {
229 return CacheBuilder.newBuilder()
230 .expireAfterWrite(10, TimeUnit.SECONDS)
231 .removalListener((RemovalNotification<Long, InternalCacheEntry> notification) -> {
232 if (notification.getCause() == RemovalCause.EXPIRED) {
233 providerService.batchOperationCompleted(notification.getKey(),
234 notification.getValue().failedCompletion());
235 }
236 }).build();
237 }
238
239 private void createCollectors() {
240 controller.getSwitches().forEach(this::createCollector);
241 }
242
243 private void createCollector(OpenFlowSwitch sw) {
Kavitha Alagesan6704df32016-08-18 15:15:31 +0530244 if (sw == null) {
245 return;
246 }
Laszlo Papp58174812018-01-16 13:29:47 +0000247 if (sw.features().getCapabilities().contains(OFCapabilities.FLOW_STATS)) {
248 if (adaptiveFlowSampling) {
249 // NewAdaptiveFlowStatsCollector Constructor
250 NewAdaptiveFlowStatsCollector fsc =
251 new NewAdaptiveFlowStatsCollector(driverService, sw, flowPollFrequency);
252 stopCollectorIfNeeded(afsCollectors.put(new Dpid(sw.getId()), fsc));
253 fsc.start();
254 } else {
255 FlowStatsCollector fsc = new FlowStatsCollector(timer, sw, flowPollFrequency);
256 stopCollectorIfNeeded(simpleCollectors.put(new Dpid(sw.getId()), fsc));
257 fsc.start();
258 }
ssyoon9030fbcd92015-08-17 10:42:07 +0900259 }
Laszlo Pappedadbe22017-12-14 20:05:49 +0000260 if (sw.features().getCapabilities().contains(OFCapabilities.TABLE_STATS)) {
261 TableStatisticsCollector tsc = new TableStatisticsCollector(timer, sw, flowPollFrequency);
262 stopCollectorIfNeeded(tableStatsCollectors.put(new Dpid(sw.getId()), tsc));
263 tsc.start();
264 }
Thomas Vachuskaa394b952016-06-14 15:02:09 -0700265 }
266
267 private void stopCollectorIfNeeded(SwitchDataCollector collector) {
268 if (collector != null) {
269 collector.stop();
270 }
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700271 }
272
273 private void stopCollectors() {
ssyoon9030fbcd92015-08-17 10:42:07 +0900274 if (adaptiveFlowSampling) {
275 // NewAdaptiveFlowStatsCollector Destructor
276 afsCollectors.values().forEach(NewAdaptiveFlowStatsCollector::stop);
277 afsCollectors.clear();
278 } else {
279 simpleCollectors.values().forEach(FlowStatsCollector::stop);
280 simpleCollectors.clear();
281 }
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700282 tableStatsCollectors.values().forEach(TableStatisticsCollector::stop);
283 tableStatsCollectors.clear();
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700284 }
285
286 private void adjustRate() {
287 DefaultLoad.setPollInterval(flowPollFrequency);
ssyoon9030fbcd92015-08-17 10:42:07 +0900288 if (adaptiveFlowSampling) {
289 // NewAdaptiveFlowStatsCollector calAndPollInterval
290 afsCollectors.values().forEach(fsc -> fsc.adjustCalAndPollInterval(flowPollFrequency));
291 } else {
292 simpleCollectors.values().forEach(fsc -> fsc.adjustPollInterval(flowPollFrequency));
293 }
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700294 tableStatsCollectors.values().forEach(tsc -> tsc.adjustPollInterval(flowPollFrequency));
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700295 }
296
alshabib1cc04f72014-09-16 16:09:58 -0700297 @Override
298 public void applyFlowRule(FlowRule... flowRules) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800299 for (FlowRule flowRule : flowRules) {
300 applyRule(flowRule);
alshabib35edb1a2014-09-16 17:44:44 -0700301 }
alshabib1cc04f72014-09-16 16:09:58 -0700302 }
303
alshabib35edb1a2014-09-16 17:44:44 -0700304 private void applyRule(FlowRule flowRule) {
ssyoon9030fbcd92015-08-17 10:42:07 +0900305 Dpid dpid = Dpid.dpid(flowRule.deviceId().uri());
306 OpenFlowSwitch sw = controller.getSwitch(dpid);
307
Ray Milkey0ae473d2016-04-04 10:56:47 -0700308 if (sw == null) {
309 return;
310 }
311
Thomas Vachuskaa6c0d042015-04-23 10:17:37 -0700312 FlowRuleExtPayLoad flowRuleExtPayLoad = flowRule.payLoad();
313 if (hasPayload(flowRuleExtPayLoad)) {
314 OFMessage msg = new ThirdPartyMessage(flowRuleExtPayLoad.payLoad());
jcc3d4e14a2015-04-21 11:32:05 +0800315 sw.sendMsg(msg);
316 return;
317 }
alshabibbdcbb102015-04-22 14:16:38 -0700318 sw.sendMsg(FlowModBuilder.builder(flowRule, sw.factory(),
Jonathan Hart3c259162015-10-21 21:31:19 -0700319 Optional.empty(), Optional.of(driverService)).buildFlowAdd());
alshabib35edb1a2014-09-16 17:44:44 -0700320 }
321
alshabib1cc04f72014-09-16 16:09:58 -0700322 @Override
323 public void removeFlowRule(FlowRule... flowRules) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800324 for (FlowRule flowRule : flowRules) {
325 removeRule(flowRule);
alshabib219ebaa2014-09-22 15:41:24 -0700326 }
alshabib1cc04f72014-09-16 16:09:58 -0700327 }
328
alshabib219ebaa2014-09-22 15:41:24 -0700329 private void removeRule(FlowRule flowRule) {
ssyoon9030fbcd92015-08-17 10:42:07 +0900330 Dpid dpid = Dpid.dpid(flowRule.deviceId().uri());
331 OpenFlowSwitch sw = controller.getSwitch(dpid);
332
Ray Milkey0ae473d2016-04-04 10:56:47 -0700333 if (sw == null) {
334 return;
335 }
336
Thomas Vachuskaa6c0d042015-04-23 10:17:37 -0700337 FlowRuleExtPayLoad flowRuleExtPayLoad = flowRule.payLoad();
338 if (hasPayload(flowRuleExtPayLoad)) {
339 OFMessage msg = new ThirdPartyMessage(flowRuleExtPayLoad.payLoad());
jcc3d4e14a2015-04-21 11:32:05 +0800340 sw.sendMsg(msg);
341 return;
342 }
alshabibbdcbb102015-04-22 14:16:38 -0700343 sw.sendMsg(FlowModBuilder.builder(flowRule, sw.factory(),
Jonathan Hart3c259162015-10-21 21:31:19 -0700344 Optional.empty(), Optional.of(driverService)).buildFlowDel());
alshabib219ebaa2014-09-22 15:41:24 -0700345 }
346
alshabiba68eb962014-09-24 20:34:13 -0700347 @Override
348 public void removeRulesById(ApplicationId id, FlowRule... flowRules) {
349 // TODO: optimize using the ApplicationId
350 removeFlowRule(flowRules);
351 }
352
alshabib193525b2014-10-08 18:58:03 -0700353 @Override
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800354 public void executeBatch(FlowRuleBatchOperation batch) {
ssyoon9030fbcd92015-08-17 10:42:07 +0900355 checkNotNull(batch);
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800356
ssyoon9030fbcd92015-08-17 10:42:07 +0900357 Dpid dpid = Dpid.dpid(batch.deviceId().uri());
358 OpenFlowSwitch sw = controller.getSwitch(dpid);
Madan Jampani84382b92016-06-22 08:26:49 -0700359
360 // If switch no longer exists, simply return.
361 if (sw == null) {
362 Set<FlowRule> failures = ImmutableSet.copyOf(Lists.transform(batch.getOperations(), e -> e.target()));
363 providerService.batchOperationCompleted(batch.id(),
364 new CompletedBatchOperation(false, failures, batch.deviceId()));
365 return;
366 }
367 pendingBatches.put(batch.id(), new InternalCacheEntry(batch));
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800368 OFFlowMod mod;
alshabib193525b2014-10-08 18:58:03 -0700369 for (FlowRuleBatchEntry fbe : batch.getOperations()) {
jcc3d4e14a2015-04-21 11:32:05 +0800370 // flow is the third party privacy flow
Thomas Vachuskaa6c0d042015-04-23 10:17:37 -0700371
372 FlowRuleExtPayLoad flowRuleExtPayLoad = fbe.target().payLoad();
373 if (hasPayload(flowRuleExtPayLoad)) {
374 OFMessage msg = new ThirdPartyMessage(flowRuleExtPayLoad.payLoad());
jcc3d4e14a2015-04-21 11:32:05 +0800375 sw.sendMsg(msg);
376 continue;
377 }
Thomas Vachuskad07c0922015-10-06 14:48:06 -0700378 FlowModBuilder builder =
Jonathan Hart3c259162015-10-21 21:31:19 -0700379 FlowModBuilder.builder(fbe.target(), sw.factory(),
380 Optional.of(batch.id()), Optional.of(driverService));
Sho SHIMIZUaba9d002015-01-29 14:51:04 -0800381 switch (fbe.operator()) {
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700382 case ADD:
383 mod = builder.buildFlowAdd();
384 break;
385 case REMOVE:
386 mod = builder.buildFlowDel();
387 break;
388 case MODIFY:
389 mod = builder.buildFlowMod();
390 break;
391 default:
392 log.error("Unsupported batch operation {}; skipping flowmod {}",
ssyoon9030fbcd92015-08-17 10:42:07 +0900393 fbe.operator(), fbe);
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700394 continue;
jcc3d4e14a2015-04-21 11:32:05 +0800395 }
Saurav Das3ea46622015-04-22 14:01:34 -0700396 sw.sendMsg(mod);
alshabib193525b2014-10-08 18:58:03 -0700397 }
jcc3d4e14a2015-04-21 11:32:05 +0800398 OFBarrierRequest.Builder builder = sw.factory().buildBarrierRequest()
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800399 .setXid(batch.id());
400 sw.sendMsg(builder.build());
alshabib193525b2014-10-08 18:58:03 -0700401 }
402
Thomas Vachuskaa6c0d042015-04-23 10:17:37 -0700403 private boolean hasPayload(FlowRuleExtPayLoad flowRuleExtPayLoad) {
404 return flowRuleExtPayLoad != null &&
405 flowRuleExtPayLoad.payLoad() != null &&
406 flowRuleExtPayLoad.payLoad().length > 0;
407 }
408
alshabib8f1cf4a2014-09-17 14:44:48 -0700409 private class InternalFlowProvider
Thomas Vachuska9b2da212014-11-10 19:30:25 -0800410 implements OpenFlowSwitchListener, OpenFlowEventListener {
alshabib8f1cf4a2014-09-17 14:44:48 -0700411
alshabib8f1cf4a2014-09-17 14:44:48 -0700412 @Override
413 public void switchAdded(Dpid dpid) {
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700414 createCollector(controller.getSwitch(dpid));
alshabib8f1cf4a2014-09-17 14:44:48 -0700415 }
416
417 @Override
418 public void switchRemoved(Dpid dpid) {
ssyoon9030fbcd92015-08-17 10:42:07 +0900419 if (adaptiveFlowSampling) {
Thomas Vachuskaa394b952016-06-14 15:02:09 -0700420 stopCollectorIfNeeded(afsCollectors.remove(dpid));
ssyoon9030fbcd92015-08-17 10:42:07 +0900421 } else {
Thomas Vachuskaa394b952016-06-14 15:02:09 -0700422 stopCollectorIfNeeded(simpleCollectors.remove(dpid));
alshabibdfc7afb2014-10-21 20:13:27 -0700423 }
Thomas Vachuskaa394b952016-06-14 15:02:09 -0700424 stopCollectorIfNeeded(tableStatsCollectors.remove(dpid));
alshabib8f1cf4a2014-09-17 14:44:48 -0700425 }
426
427 @Override
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700428 public void switchChanged(Dpid dpid) {
429 }
430
431 @Override
alshabib8f1cf4a2014-09-17 14:44:48 -0700432 public void portChanged(Dpid dpid, OFPortStatus status) {
jcc3d4e14a2015-04-21 11:32:05 +0800433 // TODO: Decide whether to evict flows internal store.
alshabib8f1cf4a2014-09-17 14:44:48 -0700434 }
435
436 @Override
437 public void handleMessage(Dpid dpid, OFMessage msg) {
Ray Milkeyada9e2d2016-04-05 16:42:35 -0700438 if (providerService == null) {
439 // We are shutting down, nothing to be done
440 return;
441 }
Jonathan Harte4e74f02016-03-03 12:57:40 -0800442 DeviceId deviceId = DeviceId.deviceId(Dpid.uri(dpid));
alshabib8f1cf4a2014-09-17 14:44:48 -0700443 switch (msg.getType()) {
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700444 case FLOW_REMOVED:
445 OFFlowRemoved removed = (OFFlowRemoved) msg;
alshabib6b5cfec2014-09-18 17:42:18 -0700446
Rodrigo Duarte Sousae37d1292017-08-29 17:01:24 -0300447 FlowEntry fr = new FlowEntryBuilder(deviceId, removed, getDriver(deviceId)).build();
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700448 providerService.flowRemoved(fr);
449 break;
450 case STATS_REPLY:
451 if (((OFStatsReply) msg).getStatsType() == OFStatsType.FLOW) {
Rodrigo Duarte Sousae37d1292017-08-29 17:01:24 -0300452 pushFlowMetrics(dpid, (OFFlowStatsReply) msg, getDriver(deviceId));
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700453 } else if (((OFStatsReply) msg).getStatsType() == OFStatsType.TABLE) {
454 pushTableStatistics(dpid, (OFTableStatsReply) msg);
Cem Türker3baff672017-10-12 15:09:01 +0300455 } else if (((OFStatsReply) msg).getStatsType() == OFStatsType.FLOW_LIGHTWEIGHT) {
456 pushFlowLightWeightMetrics(dpid, (OFFlowLightweightStatsReply) msg);
sangho89bf6fb2015-02-09 09:33:13 -0800457 }
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700458 break;
459 case BARRIER_REPLY:
460 try {
Thomas Vachuska3358af22015-05-19 18:40:34 -0700461 InternalCacheEntry entry = pendingBatches.getIfPresent(msg.getXid());
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800462 if (entry != null) {
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700463 providerService
464 .batchOperationCompleted(msg.getXid(),
465 entry.completed());
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800466 } else {
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700467 log.warn("Received unknown Barrier Reply: {}",
468 msg.getXid());
469 }
470 } finally {
471 pendingBatches.invalidate(msg.getXid());
472 }
473 break;
474 case ERROR:
Thomas Vachuska3358af22015-05-19 18:40:34 -0700475 // TODO: This needs to get suppressed in a better way.
476 if (msg instanceof OFBadRequestErrorMsg &&
477 ((OFBadRequestErrorMsg) msg).getCode() == OFBadRequestCode.BAD_TYPE) {
478 log.debug("Received error message {} from {}", msg, dpid);
479 } else {
480 log.warn("Received error message {} from {}", msg, dpid);
481 }
Prince Pereira788797e2016-08-10 11:24:14 +0530482 handleErrorMsg(deviceId, msg);
Ray Milkey4fd3ceb2015-12-10 14:43:08 -0800483 break;
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700484 default:
485 log.debug("Unhandled message type: {}", msg.getType());
alshabib8f1cf4a2014-09-17 14:44:48 -0700486 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700487 }
488
Prince Pereira788797e2016-08-10 11:24:14 +0530489 private void handleErrorMsg(DeviceId deviceId, OFMessage msg) {
Prince Pereira141ed812016-09-02 19:03:18 +0530490 InternalCacheEntry entry = pendingBatches.getIfPresent(msg.getXid());
Prince Pereira788797e2016-08-10 11:24:14 +0530491 OFErrorMsg error = (OFErrorMsg) msg;
492 OFMessage ofMessage = null;
493 switch (error.getErrType()) {
494 case BAD_ACTION:
495 OFBadActionErrorMsg baErrorMsg = (OFBadActionErrorMsg) error;
496 if (baErrorMsg.getData().getParsedMessage().isPresent()) {
497 ofMessage = baErrorMsg.getData().getParsedMessage().get();
498 }
499 break;
500 case BAD_INSTRUCTION:
501 OFBadInstructionErrorMsg biErrorMsg = (OFBadInstructionErrorMsg) error;
502 if (biErrorMsg.getData().getParsedMessage().isPresent()) {
503 ofMessage = biErrorMsg.getData().getParsedMessage().get();
504 }
505 break;
506 case BAD_MATCH:
507 OFBadMatchErrorMsg bmErrorMsg = (OFBadMatchErrorMsg) error;
508 if (bmErrorMsg.getData().getParsedMessage().isPresent()) {
509 ofMessage = bmErrorMsg.getData().getParsedMessage().get();
510 }
511 break;
512 case FLOW_MOD_FAILED:
513 OFFlowModFailedErrorMsg fmFailed = (OFFlowModFailedErrorMsg) error;
514 if (fmFailed.getData().getParsedMessage().isPresent()) {
515 ofMessage = fmFailed.getData().getParsedMessage().get();
516 }
517 break;
518 default:
519 // Do nothing.
520 return;
521 }
Prince Pereira141ed812016-09-02 19:03:18 +0530522
Prince Pereira788797e2016-08-10 11:24:14 +0530523 if (ofMessage != null) {
Prince Pereira141ed812016-09-02 19:03:18 +0530524
Prince Pereira788797e2016-08-10 11:24:14 +0530525 if (entry != null) {
526 OFFlowMod ofFlowMod = (OFFlowMod) ofMessage;
Rodrigo Duarte Sousae37d1292017-08-29 17:01:24 -0300527 entry.appendFailure(new FlowEntryBuilder(deviceId, ofFlowMod, getDriver(deviceId)).build());
Prince Pereira788797e2016-08-10 11:24:14 +0530528 } else {
529 log.error("No matching batch for this error: {}", error);
530 }
Prince Pereira141ed812016-09-02 19:03:18 +0530531
Prince Pereira788797e2016-08-10 11:24:14 +0530532 } else {
Prince Pereira141ed812016-09-02 19:03:18 +0530533
534 U64 cookieId = readCookieIdFromOFErrorMsg(error, msg.getVersion());
535
536 if (cookieId != null) {
537 long flowId = cookieId.getValue();
538
539 if (entry != null) {
540 for (FlowRuleBatchEntry fbEntry : entry.operation.getOperations()) {
541 if (fbEntry.target().id().value() == flowId) {
542 entry.appendFailure(fbEntry.target());
543 break;
544 }
545 }
546 } else {
547 log.error("No matching batch for this error: {}", error);
548 }
549
550 } else {
551 log.error("Flow installation failed but switch " +
552 "didn't tell us which one.");
553 }
Prince Pereira788797e2016-08-10 11:24:14 +0530554 }
555 }
556
Prince Pereira141ed812016-09-02 19:03:18 +0530557 /**
558 * Reading cookieId from OFErrorMsg.
559 *
560 * Loxigen OpenFlow API failed in parsing error messages because of
561 * 64 byte data truncation based on OpenFlow specs. The method written
562 * is a workaround to extract the cookieId from the packet till the
563 * issue is resolved in Loxigen OpenFlow code.
564 * Ref: https://groups.google.com/a/onosproject.org/forum/#!topic
565 * /onos-dev/_KwlHZDllLE
566 *
567 * @param msg OF error message
568 * @param ofVersion Openflow version
569 * @return cookieId
570 */
571 private U64 readCookieIdFromOFErrorMsg(OFErrorMsg msg,
572 OFVersion ofVersion) {
573
574 if (ofVersion.wireVersion < OFVersion.OF_13.wireVersion) {
575 log.debug("Unhandled error msg with OF version {} " +
576 "which is less than {}",
577 ofVersion, OFVersion.OF_13);
578 return null;
579 }
580
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700581 ByteBuf bb = Unpooled.wrappedBuffer(msg.getData().getData());
Prince Pereira141ed812016-09-02 19:03:18 +0530582
583 if (bb.readableBytes() < MIN_EXPECTED_BYTE_LEN) {
584 log.debug("Wrong length: Expected to be >= {}, was: {}",
585 MIN_EXPECTED_BYTE_LEN, bb.readableBytes());
586 return null;
587 }
588
589 byte ofVer = bb.readByte();
590
591 if (ofVer != ofVersion.wireVersion) {
592 log.debug("Wrong version: Expected={}, got={}",
593 ofVersion.wireVersion, ofVer);
594 return null;
595 }
596
597 byte type = bb.readByte();
598
599 if (type != OFType.FLOW_MOD.ordinal()) {
600 log.debug("Wrong type: Expected={}, got={}",
601 OFType.FLOW_MOD.ordinal(), type);
602 return null;
603 }
604
605 int length = U16.f(bb.readShort());
606
607 if (length < MIN_EXPECTED_BYTE_LEN) {
608 log.debug("Wrong length: Expected to be >= {}, was: {}",
609 MIN_EXPECTED_BYTE_LEN, length);
610 return null;
611 }
612
613 bb.skipBytes(SKIP_BYTES);
614 return U64.ofRaw(bb.readLong());
615 }
616
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700617 @Override
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700618 public void receivedRoleReply(Dpid dpid, RoleState requested,
Thomas Vachuska9b2da212014-11-10 19:30:25 -0800619 RoleState response) {
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700620 // Do nothing here for now.
621 }
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700622
Rodrigo Duarte Sousae37d1292017-08-29 17:01:24 -0300623 private DriverHandler getDriver(DeviceId devId) {
624 Driver driver = driverService.getDriver(devId);
625 DriverHandler handler = new DefaultDriverHandler(new DefaultDriverData(driver, devId));
626 return handler;
627 }
628
629 private void pushFlowMetrics(Dpid dpid, OFFlowStatsReply replies, DriverHandler handler) {
alshabib64def642014-12-02 23:27:37 -0800630
alshabib54ce5892014-09-23 17:50:51 -0700631 DeviceId did = DeviceId.deviceId(Dpid.uri(dpid));
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900632 NewAdaptiveFlowStatsCollector afsc = afsCollectors.get(dpid);
alshabib54ce5892014-09-23 17:50:51 -0700633
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900634 if (adaptiveFlowSampling && afsc != null) {
635 List<FlowEntry> flowEntries = replies.getEntries().stream()
Rodrigo Duarte Sousae37d1292017-08-29 17:01:24 -0300636 .map(entry -> new FlowEntryBuilder(did, entry, handler).withSetAfsc(afsc).build())
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900637 .collect(Collectors.toList());
alshabib54ce5892014-09-23 17:50:51 -0700638
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900639 // Check that OFFlowStatsReply Xid is same with the one of OFFlowStatsRequest?
640 if (afsc.getFlowMissingXid() != NewAdaptiveFlowStatsCollector.NO_FLOW_MISSING_XID) {
641 log.debug("OpenFlowRuleProvider:pushFlowMetrics, flowMissingXid={}, "
642 + "OFFlowStatsReply Xid={}, for {}",
643 afsc.getFlowMissingXid(), replies.getXid(), dpid);
644 if (afsc.getFlowMissingXid() == replies.getXid()) {
645 // call entire flow stats update with flowMissing synchronization.
646 // used existing pushFlowMetrics
647 providerService.pushFlowMetrics(did, flowEntries);
ssyoon9030fbcd92015-08-17 10:42:07 +0900648 }
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900649 // reset flowMissingXid to NO_FLOW_MISSING_XID
650 afsc.setFlowMissingXid(NewAdaptiveFlowStatsCollector.NO_FLOW_MISSING_XID);
651 } else {
652 // call individual flow stats update
653 providerService.pushFlowMetricsWithoutFlowMissing(did, flowEntries);
ssyoon9030fbcd92015-08-17 10:42:07 +0900654 }
655 } else {
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900656 List<FlowEntry> flowEntries = replies.getEntries().stream()
Rodrigo Duarte Sousae37d1292017-08-29 17:01:24 -0300657 .map(entry -> new FlowEntryBuilder(did, entry, handler).build())
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900658 .collect(Collectors.toList());
659
ssyoon9030fbcd92015-08-17 10:42:07 +0900660 // call existing entire flow stats update with flowMissing synchronization
661 providerService.pushFlowMetrics(did, flowEntries);
662 }
alshabib5c370ff2014-09-18 10:12:14 -0700663 }
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700664
665 private void pushTableStatistics(Dpid dpid, OFTableStatsReply replies) {
666
667 DeviceId did = DeviceId.deviceId(Dpid.uri(dpid));
668 List<TableStatisticsEntry> tableStatsEntries = replies.getEntries().stream()
669 .map(entry -> buildTableStatistics(did, entry))
670 .filter(Objects::nonNull)
671 .collect(Collectors.toList());
672 providerService.pushTableStatistics(did, tableStatsEntries);
673 }
674
Cem Türker3baff672017-10-12 15:09:01 +0300675 private void pushFlowLightWeightMetrics(Dpid dpid, OFFlowLightweightStatsReply replies) {
676
677 DeviceId did = DeviceId.deviceId(Dpid.uri(dpid));
678 NewAdaptiveFlowStatsCollector afsc = afsCollectors.get(dpid);
679 if (adaptiveFlowSampling && afsc != null) {
680 List<FlowEntry> flowEntries = replies.getEntries().stream()
681 .map(entry -> new FlowEntryBuilder(did, entry, driverService).withSetAfsc(afsc).build())
682 .collect(Collectors.toList());
683
684 // Check that OFFlowStatsReply Xid is same with the one of OFFlowStatsRequest?
685 if (afsc.getFlowMissingXid() != NewAdaptiveFlowStatsCollector.NO_FLOW_MISSING_XID) {
686 log.debug("OpenFlowRuleProvider:pushFlowMetrics, flowMissingXid={}, "
687 + "OFFlowStatsReply Xid={}, for {}",
688 afsc.getFlowMissingXid(), replies.getXid(), dpid);
689 if (afsc.getFlowMissingXid() == replies.getXid()) {
690 // call entire flow stats update with flowMissing synchronization.
691 // used existing pushFlowMetrics
692 providerService.pushFlowMetrics(did, flowEntries);
693 }
694 // reset flowMissingXid to NO_FLOW_MISSING_XID
695 afsc.setFlowMissingXid(NewAdaptiveFlowStatsCollector.NO_FLOW_MISSING_XID);
696 } else {
697 // call individual flow stats update
698 providerService.pushFlowMetricsWithoutFlowMissing(did, flowEntries);
699 }
700 } else {
701 List<FlowEntry> flowEntries = replies.getEntries().stream()
702 .map(entry -> new FlowEntryBuilder(did, entry, driverService).build())
703 .collect(Collectors.toList());
704 // call existing entire flow stats update with flowMissing synchronization
705 providerService.pushFlowMetrics(did, flowEntries);
706 }
707 }
708
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700709 private TableStatisticsEntry buildTableStatistics(DeviceId deviceId,
710 OFTableStatsEntry ofEntry) {
711 TableStatisticsEntry entry = null;
712 if (ofEntry != null) {
hjtsao1a4333c2018-10-22 11:02:00 -0700713 IndexTableId tid = IndexTableId.of(ofEntry.getTableId().getValue());
714
715 try {
716 entry = DefaultTableStatisticsEntry.builder()
717 .withDeviceId(deviceId)
718 .withTableId(tid)
719 .withActiveFlowEntries(ofEntry.getActiveCount())
720 .withPacketsLookedUpCount(ofEntry.getLookupCount().getValue())
721 .withPacketsMatchedCount(ofEntry.getMatchedCount().getValue())
722 .withMaxSize(ofEntry.getMaxEntries()).build();
723 } catch (UnsupportedOperationException e) {
724 // The exception "UnsupportedOperationException" is thrown by "getMaxEntries()".
725 entry = DefaultTableStatisticsEntry.builder()
726 .withDeviceId(deviceId)
727 .withTableId(tid)
728 .withActiveFlowEntries(ofEntry.getActiveCount())
729 .withPacketsLookedUpCount(ofEntry.getLookupCount().getValue())
730 .withPacketsMatchedCount(ofEntry.getMatchedCount().getValue()).build();
731 }
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700732 }
733
734 return entry;
735
736 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700737 }
alshabib1cc04f72014-09-16 16:09:58 -0700738
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800739 /**
jcc3d4e14a2015-04-21 11:32:05 +0800740 * The internal cache entry holding the original request as well as
741 * accumulating the any failures along the way.
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700742 * <p/>
jcc3d4e14a2015-04-21 11:32:05 +0800743 * If this entry is evicted from the cache then the entire operation is
744 * considered failed. Otherwise, only the failures reported by the device
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800745 * will be propagated up.
746 */
747 private class InternalCacheEntry {
alshabib902d41b2014-10-07 16:52:05 -0700748
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800749 private final FlowRuleBatchOperation operation;
750 private final Set<FlowRule> failures = Sets.newConcurrentHashSet();
alshabib193525b2014-10-08 18:58:03 -0700751
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800752 public InternalCacheEntry(FlowRuleBatchOperation operation) {
753 this.operation = operation;
alshabib902d41b2014-10-07 16:52:05 -0700754 }
755
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800756 /**
757 * Appends a failed rule to the set of failed items.
jcc3d4e14a2015-04-21 11:32:05 +0800758 *
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800759 * @param rule the failed rule
760 */
761 public void appendFailure(FlowRule rule) {
762 failures.add(rule);
Thomas Vachuska9b2da212014-11-10 19:30:25 -0800763 }
764
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800765 /**
766 * Fails the entire batch and returns the failed operation.
jcc3d4e14a2015-04-21 11:32:05 +0800767 *
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800768 * @return the failed operation
769 */
770 public CompletedBatchOperation failedCompletion() {
771 Set<FlowRule> fails = operation.getOperations().stream()
772 .map(op -> op.target()).collect(Collectors.toSet());
jcc3d4e14a2015-04-21 11:32:05 +0800773 return new CompletedBatchOperation(false,
774 Collections
775 .unmodifiableSet(fails),
776 operation.deviceId());
alshabib902d41b2014-10-07 16:52:05 -0700777 }
778
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800779 /**
780 * Returns the completed operation and whether the batch suceeded.
jcc3d4e14a2015-04-21 11:32:05 +0800781 *
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800782 * @return the completed operation
783 */
784 public CompletedBatchOperation completed() {
jcc3d4e14a2015-04-21 11:32:05 +0800785 return new CompletedBatchOperation(
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700786 failures.isEmpty(),
787 Collections
788 .unmodifiableSet(failures),
789 operation.deviceId());
alshabib902d41b2014-10-07 16:52:05 -0700790 }
alshabib902d41b2014-10-07 16:52:05 -0700791 }
alshabiba68eb962014-09-24 20:34:13 -0700792
hjtsao1a4333c2018-10-22 11:02:00 -0700793}