blob: ef013b594abd2b77326abe71e0505a36d86fecfe [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;
Brian O'Connorabafb502014-12-02 22:26:20 -080055import org.onosproject.net.provider.AbstractProvider;
56import org.onosproject.net.provider.ProviderId;
Thomas Vachuska75aaa672015-04-29 12:24:43 -070057import org.onosproject.net.statistic.DefaultLoad;
Brian O'Connorabafb502014-12-02 22:26:20 -080058import org.onosproject.openflow.controller.Dpid;
59import org.onosproject.openflow.controller.OpenFlowController;
60import org.onosproject.openflow.controller.OpenFlowEventListener;
61import org.onosproject.openflow.controller.OpenFlowSwitch;
62import org.onosproject.openflow.controller.OpenFlowSwitchListener;
63import org.onosproject.openflow.controller.RoleState;
jcc3d4e14a2015-04-21 11:32:05 +080064import org.onosproject.openflow.controller.ThirdPartyMessage;
Thomas Vachuska95caba32016-04-04 10:42:05 -070065import org.onosproject.provider.of.flow.util.FlowEntryBuilder;
Thomas Vachuska75aaa672015-04-29 12:24:43 -070066import org.osgi.service.component.ComponentContext;
Thomas Vachuska3358af22015-05-19 18:40:34 -070067import org.projectfloodlight.openflow.protocol.OFBadRequestCode;
alshabib902d41b2014-10-07 16:52:05 -070068import org.projectfloodlight.openflow.protocol.OFBarrierRequest;
Laszlo Pappedadbe22017-12-14 20:05:49 +000069import org.projectfloodlight.openflow.protocol.OFCapabilities;
alshabib902d41b2014-10-07 16:52:05 -070070import org.projectfloodlight.openflow.protocol.OFErrorMsg;
Cem Türker3baff672017-10-12 15:09:01 +030071import org.projectfloodlight.openflow.protocol.OFFlowLightweightStatsReply;
alshabib193525b2014-10-08 18:58:03 -070072import org.projectfloodlight.openflow.protocol.OFFlowMod;
alshabib8f1cf4a2014-09-17 14:44:48 -070073import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
alshabib5c370ff2014-09-18 10:12:14 -070074import org.projectfloodlight.openflow.protocol.OFFlowStatsReply;
alshabib8f1cf4a2014-09-17 14:44:48 -070075import org.projectfloodlight.openflow.protocol.OFMessage;
76import org.projectfloodlight.openflow.protocol.OFPortStatus;
alshabib5c370ff2014-09-18 10:12:14 -070077import org.projectfloodlight.openflow.protocol.OFStatsReply;
sangho89bf6fb2015-02-09 09:33:13 -080078import org.projectfloodlight.openflow.protocol.OFStatsType;
Jonathan Hart3c259162015-10-21 21:31:19 -070079import org.projectfloodlight.openflow.protocol.OFTableStatsEntry;
80import org.projectfloodlight.openflow.protocol.OFTableStatsReply;
Prince Pereira141ed812016-09-02 19:03:18 +053081import org.projectfloodlight.openflow.protocol.OFType;
82import org.projectfloodlight.openflow.protocol.OFVersion;
Prince Pereira788797e2016-08-10 11:24:14 +053083import org.projectfloodlight.openflow.protocol.errormsg.OFBadActionErrorMsg;
84import org.projectfloodlight.openflow.protocol.errormsg.OFBadInstructionErrorMsg;
85import org.projectfloodlight.openflow.protocol.errormsg.OFBadMatchErrorMsg;
Thomas Vachuska3358af22015-05-19 18:40:34 -070086import org.projectfloodlight.openflow.protocol.errormsg.OFBadRequestErrorMsg;
alshabib193525b2014-10-08 18:58:03 -070087import org.projectfloodlight.openflow.protocol.errormsg.OFFlowModFailedErrorMsg;
Prince Pereira141ed812016-09-02 19:03:18 +053088import org.projectfloodlight.openflow.types.U16;
89import org.projectfloodlight.openflow.types.U64;
alshabib1cc04f72014-09-16 16:09:58 -070090import org.slf4j.Logger;
91
Thomas Vachuska75aaa672015-04-29 12:24:43 -070092import java.util.Collections;
93import java.util.Dictionary;
94import java.util.List;
95import java.util.Map;
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -070096import java.util.Objects;
Thomas Vachuska75aaa672015-04-29 12:24:43 -070097import java.util.Optional;
98import java.util.Set;
99import java.util.Timer;
100import java.util.concurrent.TimeUnit;
101import java.util.stream.Collectors;
102
ssyoon9030fbcd92015-08-17 10:42:07 +0900103import static com.google.common.base.Preconditions.checkNotNull;
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700104import static com.google.common.base.Strings.isNullOrEmpty;
105import static org.onlab.util.Tools.get;
106import static org.slf4j.LoggerFactory.getLogger;
alshabibeec3a062014-09-17 18:01:26 -0700107
alshabib1cc04f72014-09-16 16:09:58 -0700108/**
jcc3d4e14a2015-04-21 11:32:05 +0800109 * Provider which uses an OpenFlow controller to detect network end-station
110 * hosts.
alshabib1cc04f72014-09-16 16:09:58 -0700111 */
112@Component(immediate = true)
jcc3d4e14a2015-04-21 11:32:05 +0800113public class OpenFlowRuleProvider extends AbstractProvider
114 implements FlowRuleProvider {
alshabib1cc04f72014-09-16 16:09:58 -0700115
116 private final Logger log = getLogger(getClass());
117
118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
119 protected FlowRuleProviderRegistry providerRegistry;
120
121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
122 protected OpenFlowController controller;
123
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
125 protected ComponentConfigService cfgService;
126
Jonathan Hart3c259162015-10-21 21:31:19 -0700127 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
128 protected DriverService driverService;
129
ssyoon9030fbcd92015-08-17 10:42:07 +0900130 private static final int DEFAULT_POLL_FREQUENCY = 5;
Prince Pereira141ed812016-09-02 19:03:18 +0530131 private static final int MIN_EXPECTED_BYTE_LEN = 56;
132 private static final int SKIP_BYTES = 4;
133 private static final boolean DEFAULT_ADAPTIVE_FLOW_SAMPLING = false;
134
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700135 @Property(name = "flowPollFrequency", intValue = DEFAULT_POLL_FREQUENCY,
136 label = "Frequency (in seconds) for polling flow statistics")
137 private int flowPollFrequency = DEFAULT_POLL_FREQUENCY;
138
ssyoon9030fbcd92015-08-17 10:42:07 +0900139 @Property(name = "adaptiveFlowSampling", boolValue = DEFAULT_ADAPTIVE_FLOW_SAMPLING,
140 label = "Adaptive Flow Sampling is on or off")
141 private boolean adaptiveFlowSampling = DEFAULT_ADAPTIVE_FLOW_SAMPLING;
142
alshabib1cc04f72014-09-16 16:09:58 -0700143 private FlowRuleProviderService providerService;
144
alshabibeec3a062014-09-17 18:01:26 -0700145 private final InternalFlowProvider listener = new InternalFlowProvider();
146
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800147 private Cache<Long, InternalCacheEntry> pendingBatches;
alshabib193525b2014-10-08 18:58:03 -0700148
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900149 private final Timer timer = new Timer("onos-openflow-collector");
150
Cem Türker3baff672017-10-12 15:09:01 +0300151
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900152 // Old simple collector set
Madan Jampani6b266102016-06-23 00:56:36 -0700153 private final Map<Dpid, FlowStatsCollector> simpleCollectors = Maps.newConcurrentMap();
ssyoon9030fbcd92015-08-17 10:42:07 +0900154
155 // NewAdaptiveFlowStatsCollector Set
Madan Jampani6b266102016-06-23 00:56:36 -0700156 private final Map<Dpid, NewAdaptiveFlowStatsCollector> afsCollectors = Maps.newConcurrentMap();
157 private final Map<Dpid, TableStatisticsCollector> tableStatsCollectors = Maps.newConcurrentMap();
alshabib3d643ec2014-10-22 18:33:00 -0700158
alshabib1cc04f72014-09-16 16:09:58 -0700159 /**
160 * Creates an OpenFlow host provider.
161 */
162 public OpenFlowRuleProvider() {
Brian O'Connorabafb502014-12-02 22:26:20 -0800163 super(new ProviderId("of", "org.onosproject.provider.openflow"));
alshabib1cc04f72014-09-16 16:09:58 -0700164 }
165
166 @Activate
Thomas Vachuskaa394b952016-06-14 15:02:09 -0700167 protected void activate(ComponentContext context) {
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700168 cfgService.registerProperties(getClass());
alshabib1cc04f72014-09-16 16:09:58 -0700169 providerService = providerRegistry.register(this);
alshabibeec3a062014-09-17 18:01:26 -0700170 controller.addListener(listener);
171 controller.addEventListener(listener);
alshabib3d643ec2014-10-22 18:33:00 -0700172
Antonio Marsico1c5ae1f2015-12-15 15:31:56 +0100173 modified(context);
174
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700175 pendingBatches = createBatchCache();
ssyoon9030fbcd92015-08-17 10:42:07 +0900176
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700177 createCollectors();
alshabib3d643ec2014-10-22 18:33:00 -0700178
ssyoon9030fbcd92015-08-17 10:42:07 +0900179 log.info("Started with flowPollFrequency = {}, adaptiveFlowSampling = {}",
180 flowPollFrequency, adaptiveFlowSampling);
alshabib1cc04f72014-09-16 16:09:58 -0700181 }
182
183 @Deactivate
Thomas Vachuskaa394b952016-06-14 15:02:09 -0700184 protected void deactivate(ComponentContext context) {
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700185 cfgService.unregisterProperties(getClass(), false);
186 stopCollectors();
alshabib1cc04f72014-09-16 16:09:58 -0700187 providerRegistry.unregister(this);
188 providerService = null;
189
190 log.info("Stopped");
191 }
Thomas Vachuska9b2da212014-11-10 19:30:25 -0800192
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700193 @Modified
Thomas Vachuskaa394b952016-06-14 15:02:09 -0700194 protected void modified(ComponentContext context) {
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700195 Dictionary<?, ?> properties = context.getProperties();
196 int newFlowPollFrequency;
197 try {
198 String s = get(properties, "flowPollFrequency");
199 newFlowPollFrequency = isNullOrEmpty(s) ? flowPollFrequency : Integer.parseInt(s.trim());
200
201 } catch (NumberFormatException | ClassCastException e) {
202 newFlowPollFrequency = flowPollFrequency;
203 }
204
205 if (newFlowPollFrequency != flowPollFrequency) {
206 flowPollFrequency = newFlowPollFrequency;
207 adjustRate();
208 }
209
210 log.info("Settings: flowPollFrequency={}", flowPollFrequency);
ssyoon9030fbcd92015-08-17 10:42:07 +0900211
212 boolean newAdaptiveFlowSampling;
213 String s = get(properties, "adaptiveFlowSampling");
214 newAdaptiveFlowSampling = isNullOrEmpty(s) ? adaptiveFlowSampling : Boolean.parseBoolean(s.trim());
215
216 if (newAdaptiveFlowSampling != adaptiveFlowSampling) {
217 // stop previous collector
218 stopCollectors();
219 adaptiveFlowSampling = newAdaptiveFlowSampling;
220 // create new collectors
221 createCollectors();
222 }
223
224 log.info("Settings: adaptiveFlowSampling={}", adaptiveFlowSampling);
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700225 }
226
227 private Cache<Long, InternalCacheEntry> createBatchCache() {
228 return CacheBuilder.newBuilder()
229 .expireAfterWrite(10, TimeUnit.SECONDS)
230 .removalListener((RemovalNotification<Long, InternalCacheEntry> notification) -> {
231 if (notification.getCause() == RemovalCause.EXPIRED) {
232 providerService.batchOperationCompleted(notification.getKey(),
233 notification.getValue().failedCompletion());
234 }
235 }).build();
236 }
237
238 private void createCollectors() {
239 controller.getSwitches().forEach(this::createCollector);
240 }
241
242 private void createCollector(OpenFlowSwitch sw) {
Kavitha Alagesan6704df32016-08-18 15:15:31 +0530243 if (sw == null) {
244 return;
245 }
ssyoon9030fbcd92015-08-17 10:42:07 +0900246 if (adaptiveFlowSampling) {
247 // NewAdaptiveFlowStatsCollector Constructor
Charles Chan14967c22015-12-07 11:11:50 -0800248 NewAdaptiveFlowStatsCollector fsc =
249 new NewAdaptiveFlowStatsCollector(driverService, sw, flowPollFrequency);
Thomas Vachuskaa394b952016-06-14 15:02:09 -0700250 stopCollectorIfNeeded(afsCollectors.put(new Dpid(sw.getId()), fsc));
Palash Kalaa439afe2017-05-16 14:53:15 +0900251 fsc.start();
ssyoon9030fbcd92015-08-17 10:42:07 +0900252 } else {
253 FlowStatsCollector fsc = new FlowStatsCollector(timer, sw, flowPollFrequency);
Thomas Vachuskaa394b952016-06-14 15:02:09 -0700254 stopCollectorIfNeeded(simpleCollectors.put(new Dpid(sw.getId()), fsc));
Palash Kalaa439afe2017-05-16 14:53:15 +0900255 fsc.start();
ssyoon9030fbcd92015-08-17 10:42:07 +0900256 }
Laszlo Pappedadbe22017-12-14 20:05:49 +0000257 if (sw.features().getCapabilities().contains(OFCapabilities.TABLE_STATS)) {
258 TableStatisticsCollector tsc = new TableStatisticsCollector(timer, sw, flowPollFrequency);
259 stopCollectorIfNeeded(tableStatsCollectors.put(new Dpid(sw.getId()), tsc));
260 tsc.start();
261 }
Thomas Vachuskaa394b952016-06-14 15:02:09 -0700262 }
263
264 private void stopCollectorIfNeeded(SwitchDataCollector collector) {
265 if (collector != null) {
266 collector.stop();
267 }
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700268 }
269
270 private void stopCollectors() {
ssyoon9030fbcd92015-08-17 10:42:07 +0900271 if (adaptiveFlowSampling) {
272 // NewAdaptiveFlowStatsCollector Destructor
273 afsCollectors.values().forEach(NewAdaptiveFlowStatsCollector::stop);
274 afsCollectors.clear();
275 } else {
276 simpleCollectors.values().forEach(FlowStatsCollector::stop);
277 simpleCollectors.clear();
278 }
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700279 tableStatsCollectors.values().forEach(TableStatisticsCollector::stop);
280 tableStatsCollectors.clear();
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700281 }
282
283 private void adjustRate() {
284 DefaultLoad.setPollInterval(flowPollFrequency);
ssyoon9030fbcd92015-08-17 10:42:07 +0900285 if (adaptiveFlowSampling) {
286 // NewAdaptiveFlowStatsCollector calAndPollInterval
287 afsCollectors.values().forEach(fsc -> fsc.adjustCalAndPollInterval(flowPollFrequency));
288 } else {
289 simpleCollectors.values().forEach(fsc -> fsc.adjustPollInterval(flowPollFrequency));
290 }
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700291 tableStatsCollectors.values().forEach(tsc -> tsc.adjustPollInterval(flowPollFrequency));
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700292 }
293
alshabib1cc04f72014-09-16 16:09:58 -0700294 @Override
295 public void applyFlowRule(FlowRule... flowRules) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800296 for (FlowRule flowRule : flowRules) {
297 applyRule(flowRule);
alshabib35edb1a2014-09-16 17:44:44 -0700298 }
alshabib1cc04f72014-09-16 16:09:58 -0700299 }
300
alshabib35edb1a2014-09-16 17:44:44 -0700301 private void applyRule(FlowRule flowRule) {
ssyoon9030fbcd92015-08-17 10:42:07 +0900302 Dpid dpid = Dpid.dpid(flowRule.deviceId().uri());
303 OpenFlowSwitch sw = controller.getSwitch(dpid);
304
Ray Milkey0ae473d2016-04-04 10:56:47 -0700305 if (sw == null) {
306 return;
307 }
308
Thomas Vachuskaa6c0d042015-04-23 10:17:37 -0700309 FlowRuleExtPayLoad flowRuleExtPayLoad = flowRule.payLoad();
310 if (hasPayload(flowRuleExtPayLoad)) {
311 OFMessage msg = new ThirdPartyMessage(flowRuleExtPayLoad.payLoad());
jcc3d4e14a2015-04-21 11:32:05 +0800312 sw.sendMsg(msg);
313 return;
314 }
alshabibbdcbb102015-04-22 14:16:38 -0700315 sw.sendMsg(FlowModBuilder.builder(flowRule, sw.factory(),
Jonathan Hart3c259162015-10-21 21:31:19 -0700316 Optional.empty(), Optional.of(driverService)).buildFlowAdd());
alshabib35edb1a2014-09-16 17:44:44 -0700317 }
318
alshabib1cc04f72014-09-16 16:09:58 -0700319 @Override
320 public void removeFlowRule(FlowRule... flowRules) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800321 for (FlowRule flowRule : flowRules) {
322 removeRule(flowRule);
alshabib219ebaa2014-09-22 15:41:24 -0700323 }
alshabib1cc04f72014-09-16 16:09:58 -0700324 }
325
alshabib219ebaa2014-09-22 15:41:24 -0700326 private void removeRule(FlowRule flowRule) {
ssyoon9030fbcd92015-08-17 10:42:07 +0900327 Dpid dpid = Dpid.dpid(flowRule.deviceId().uri());
328 OpenFlowSwitch sw = controller.getSwitch(dpid);
329
Ray Milkey0ae473d2016-04-04 10:56:47 -0700330 if (sw == null) {
331 return;
332 }
333
Thomas Vachuskaa6c0d042015-04-23 10:17:37 -0700334 FlowRuleExtPayLoad flowRuleExtPayLoad = flowRule.payLoad();
335 if (hasPayload(flowRuleExtPayLoad)) {
336 OFMessage msg = new ThirdPartyMessage(flowRuleExtPayLoad.payLoad());
jcc3d4e14a2015-04-21 11:32:05 +0800337 sw.sendMsg(msg);
338 return;
339 }
alshabibbdcbb102015-04-22 14:16:38 -0700340 sw.sendMsg(FlowModBuilder.builder(flowRule, sw.factory(),
Jonathan Hart3c259162015-10-21 21:31:19 -0700341 Optional.empty(), Optional.of(driverService)).buildFlowDel());
alshabib219ebaa2014-09-22 15:41:24 -0700342 }
343
alshabiba68eb962014-09-24 20:34:13 -0700344 @Override
345 public void removeRulesById(ApplicationId id, FlowRule... flowRules) {
346 // TODO: optimize using the ApplicationId
347 removeFlowRule(flowRules);
348 }
349
alshabib193525b2014-10-08 18:58:03 -0700350 @Override
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800351 public void executeBatch(FlowRuleBatchOperation batch) {
ssyoon9030fbcd92015-08-17 10:42:07 +0900352 checkNotNull(batch);
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800353
ssyoon9030fbcd92015-08-17 10:42:07 +0900354 Dpid dpid = Dpid.dpid(batch.deviceId().uri());
355 OpenFlowSwitch sw = controller.getSwitch(dpid);
Madan Jampani84382b92016-06-22 08:26:49 -0700356
357 // If switch no longer exists, simply return.
358 if (sw == null) {
359 Set<FlowRule> failures = ImmutableSet.copyOf(Lists.transform(batch.getOperations(), e -> e.target()));
360 providerService.batchOperationCompleted(batch.id(),
361 new CompletedBatchOperation(false, failures, batch.deviceId()));
362 return;
363 }
364 pendingBatches.put(batch.id(), new InternalCacheEntry(batch));
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800365 OFFlowMod mod;
alshabib193525b2014-10-08 18:58:03 -0700366 for (FlowRuleBatchEntry fbe : batch.getOperations()) {
jcc3d4e14a2015-04-21 11:32:05 +0800367 // flow is the third party privacy flow
Thomas Vachuskaa6c0d042015-04-23 10:17:37 -0700368
369 FlowRuleExtPayLoad flowRuleExtPayLoad = fbe.target().payLoad();
370 if (hasPayload(flowRuleExtPayLoad)) {
371 OFMessage msg = new ThirdPartyMessage(flowRuleExtPayLoad.payLoad());
jcc3d4e14a2015-04-21 11:32:05 +0800372 sw.sendMsg(msg);
373 continue;
374 }
Thomas Vachuskad07c0922015-10-06 14:48:06 -0700375 FlowModBuilder builder =
Jonathan Hart3c259162015-10-21 21:31:19 -0700376 FlowModBuilder.builder(fbe.target(), sw.factory(),
377 Optional.of(batch.id()), Optional.of(driverService));
Thomas Vachuskad07c0922015-10-06 14:48:06 -0700378 NewAdaptiveFlowStatsCollector collector = afsCollectors.get(dpid);
Sho SHIMIZUaba9d002015-01-29 14:51:04 -0800379 switch (fbe.operator()) {
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700380 case ADD:
381 mod = builder.buildFlowAdd();
382 break;
383 case REMOVE:
384 mod = builder.buildFlowDel();
385 break;
386 case MODIFY:
387 mod = builder.buildFlowMod();
388 break;
389 default:
390 log.error("Unsupported batch operation {}; skipping flowmod {}",
ssyoon9030fbcd92015-08-17 10:42:07 +0900391 fbe.operator(), fbe);
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700392 continue;
jcc3d4e14a2015-04-21 11:32:05 +0800393 }
Saurav Das3ea46622015-04-22 14:01:34 -0700394 sw.sendMsg(mod);
alshabib193525b2014-10-08 18:58:03 -0700395 }
jcc3d4e14a2015-04-21 11:32:05 +0800396 OFBarrierRequest.Builder builder = sw.factory().buildBarrierRequest()
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800397 .setXid(batch.id());
398 sw.sendMsg(builder.build());
alshabib193525b2014-10-08 18:58:03 -0700399 }
400
Thomas Vachuskaa6c0d042015-04-23 10:17:37 -0700401 private boolean hasPayload(FlowRuleExtPayLoad flowRuleExtPayLoad) {
402 return flowRuleExtPayLoad != null &&
403 flowRuleExtPayLoad.payLoad() != null &&
404 flowRuleExtPayLoad.payLoad().length > 0;
405 }
406
alshabib8f1cf4a2014-09-17 14:44:48 -0700407 private class InternalFlowProvider
Thomas Vachuska9b2da212014-11-10 19:30:25 -0800408 implements OpenFlowSwitchListener, OpenFlowEventListener {
alshabib8f1cf4a2014-09-17 14:44:48 -0700409
alshabib8f1cf4a2014-09-17 14:44:48 -0700410 @Override
411 public void switchAdded(Dpid dpid) {
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700412 createCollector(controller.getSwitch(dpid));
alshabib8f1cf4a2014-09-17 14:44:48 -0700413 }
414
415 @Override
416 public void switchRemoved(Dpid dpid) {
ssyoon9030fbcd92015-08-17 10:42:07 +0900417 if (adaptiveFlowSampling) {
Thomas Vachuskaa394b952016-06-14 15:02:09 -0700418 stopCollectorIfNeeded(afsCollectors.remove(dpid));
ssyoon9030fbcd92015-08-17 10:42:07 +0900419 } else {
Thomas Vachuskaa394b952016-06-14 15:02:09 -0700420 stopCollectorIfNeeded(simpleCollectors.remove(dpid));
alshabibdfc7afb2014-10-21 20:13:27 -0700421 }
Thomas Vachuskaa394b952016-06-14 15:02:09 -0700422 stopCollectorIfNeeded(tableStatsCollectors.remove(dpid));
alshabib8f1cf4a2014-09-17 14:44:48 -0700423 }
424
425 @Override
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700426 public void switchChanged(Dpid dpid) {
427 }
428
429 @Override
alshabib8f1cf4a2014-09-17 14:44:48 -0700430 public void portChanged(Dpid dpid, OFPortStatus status) {
jcc3d4e14a2015-04-21 11:32:05 +0800431 // TODO: Decide whether to evict flows internal store.
alshabib8f1cf4a2014-09-17 14:44:48 -0700432 }
433
434 @Override
435 public void handleMessage(Dpid dpid, OFMessage msg) {
Ray Milkeyada9e2d2016-04-05 16:42:35 -0700436 if (providerService == null) {
437 // We are shutting down, nothing to be done
438 return;
439 }
Jonathan Harte4e74f02016-03-03 12:57:40 -0800440 DeviceId deviceId = DeviceId.deviceId(Dpid.uri(dpid));
alshabib8f1cf4a2014-09-17 14:44:48 -0700441 switch (msg.getType()) {
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700442 case FLOW_REMOVED:
443 OFFlowRemoved removed = (OFFlowRemoved) msg;
alshabib6b5cfec2014-09-18 17:42:18 -0700444
Rodrigo Duarte Sousae37d1292017-08-29 17:01:24 -0300445 FlowEntry fr = new FlowEntryBuilder(deviceId, removed, getDriver(deviceId)).build();
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700446 providerService.flowRemoved(fr);
447 break;
448 case STATS_REPLY:
449 if (((OFStatsReply) msg).getStatsType() == OFStatsType.FLOW) {
Rodrigo Duarte Sousae37d1292017-08-29 17:01:24 -0300450 pushFlowMetrics(dpid, (OFFlowStatsReply) msg, getDriver(deviceId));
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700451 } else if (((OFStatsReply) msg).getStatsType() == OFStatsType.TABLE) {
452 pushTableStatistics(dpid, (OFTableStatsReply) msg);
Cem Türker3baff672017-10-12 15:09:01 +0300453 } else if (((OFStatsReply) msg).getStatsType() == OFStatsType.FLOW_LIGHTWEIGHT) {
454 pushFlowLightWeightMetrics(dpid, (OFFlowLightweightStatsReply) msg);
sangho89bf6fb2015-02-09 09:33:13 -0800455 }
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700456 break;
457 case BARRIER_REPLY:
458 try {
Thomas Vachuska3358af22015-05-19 18:40:34 -0700459 InternalCacheEntry entry = pendingBatches.getIfPresent(msg.getXid());
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800460 if (entry != null) {
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700461 providerService
462 .batchOperationCompleted(msg.getXid(),
463 entry.completed());
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800464 } else {
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700465 log.warn("Received unknown Barrier Reply: {}",
466 msg.getXid());
467 }
468 } finally {
469 pendingBatches.invalidate(msg.getXid());
470 }
471 break;
472 case ERROR:
Thomas Vachuska3358af22015-05-19 18:40:34 -0700473 // TODO: This needs to get suppressed in a better way.
474 if (msg instanceof OFBadRequestErrorMsg &&
475 ((OFBadRequestErrorMsg) msg).getCode() == OFBadRequestCode.BAD_TYPE) {
476 log.debug("Received error message {} from {}", msg, dpid);
477 } else {
478 log.warn("Received error message {} from {}", msg, dpid);
479 }
Prince Pereira788797e2016-08-10 11:24:14 +0530480 handleErrorMsg(deviceId, msg);
Ray Milkey4fd3ceb2015-12-10 14:43:08 -0800481 break;
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700482 default:
483 log.debug("Unhandled message type: {}", msg.getType());
alshabib8f1cf4a2014-09-17 14:44:48 -0700484 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700485 }
486
Prince Pereira788797e2016-08-10 11:24:14 +0530487 private void handleErrorMsg(DeviceId deviceId, OFMessage msg) {
Prince Pereira141ed812016-09-02 19:03:18 +0530488 InternalCacheEntry entry = pendingBatches.getIfPresent(msg.getXid());
Prince Pereira788797e2016-08-10 11:24:14 +0530489 OFErrorMsg error = (OFErrorMsg) msg;
490 OFMessage ofMessage = null;
491 switch (error.getErrType()) {
492 case BAD_ACTION:
493 OFBadActionErrorMsg baErrorMsg = (OFBadActionErrorMsg) error;
494 if (baErrorMsg.getData().getParsedMessage().isPresent()) {
495 ofMessage = baErrorMsg.getData().getParsedMessage().get();
496 }
497 break;
498 case BAD_INSTRUCTION:
499 OFBadInstructionErrorMsg biErrorMsg = (OFBadInstructionErrorMsg) error;
500 if (biErrorMsg.getData().getParsedMessage().isPresent()) {
501 ofMessage = biErrorMsg.getData().getParsedMessage().get();
502 }
503 break;
504 case BAD_MATCH:
505 OFBadMatchErrorMsg bmErrorMsg = (OFBadMatchErrorMsg) error;
506 if (bmErrorMsg.getData().getParsedMessage().isPresent()) {
507 ofMessage = bmErrorMsg.getData().getParsedMessage().get();
508 }
509 break;
510 case FLOW_MOD_FAILED:
511 OFFlowModFailedErrorMsg fmFailed = (OFFlowModFailedErrorMsg) error;
512 if (fmFailed.getData().getParsedMessage().isPresent()) {
513 ofMessage = fmFailed.getData().getParsedMessage().get();
514 }
515 break;
516 default:
517 // Do nothing.
518 return;
519 }
Prince Pereira141ed812016-09-02 19:03:18 +0530520
Prince Pereira788797e2016-08-10 11:24:14 +0530521 if (ofMessage != null) {
Prince Pereira141ed812016-09-02 19:03:18 +0530522
Prince Pereira788797e2016-08-10 11:24:14 +0530523 if (entry != null) {
524 OFFlowMod ofFlowMod = (OFFlowMod) ofMessage;
Rodrigo Duarte Sousae37d1292017-08-29 17:01:24 -0300525 entry.appendFailure(new FlowEntryBuilder(deviceId, ofFlowMod, getDriver(deviceId)).build());
Prince Pereira788797e2016-08-10 11:24:14 +0530526 } else {
527 log.error("No matching batch for this error: {}", error);
528 }
Prince Pereira141ed812016-09-02 19:03:18 +0530529
Prince Pereira788797e2016-08-10 11:24:14 +0530530 } else {
Prince Pereira141ed812016-09-02 19:03:18 +0530531
532 U64 cookieId = readCookieIdFromOFErrorMsg(error, msg.getVersion());
533
534 if (cookieId != null) {
535 long flowId = cookieId.getValue();
536
537 if (entry != null) {
538 for (FlowRuleBatchEntry fbEntry : entry.operation.getOperations()) {
539 if (fbEntry.target().id().value() == flowId) {
540 entry.appendFailure(fbEntry.target());
541 break;
542 }
543 }
544 } else {
545 log.error("No matching batch for this error: {}", error);
546 }
547
548 } else {
549 log.error("Flow installation failed but switch " +
550 "didn't tell us which one.");
551 }
Prince Pereira788797e2016-08-10 11:24:14 +0530552 }
553 }
554
Prince Pereira141ed812016-09-02 19:03:18 +0530555 /**
556 * Reading cookieId from OFErrorMsg.
557 *
558 * Loxigen OpenFlow API failed in parsing error messages because of
559 * 64 byte data truncation based on OpenFlow specs. The method written
560 * is a workaround to extract the cookieId from the packet till the
561 * issue is resolved in Loxigen OpenFlow code.
562 * Ref: https://groups.google.com/a/onosproject.org/forum/#!topic
563 * /onos-dev/_KwlHZDllLE
564 *
565 * @param msg OF error message
566 * @param ofVersion Openflow version
567 * @return cookieId
568 */
569 private U64 readCookieIdFromOFErrorMsg(OFErrorMsg msg,
570 OFVersion ofVersion) {
571
572 if (ofVersion.wireVersion < OFVersion.OF_13.wireVersion) {
573 log.debug("Unhandled error msg with OF version {} " +
574 "which is less than {}",
575 ofVersion, OFVersion.OF_13);
576 return null;
577 }
578
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700579 ByteBuf bb = Unpooled.wrappedBuffer(msg.getData().getData());
Prince Pereira141ed812016-09-02 19:03:18 +0530580
581 if (bb.readableBytes() < MIN_EXPECTED_BYTE_LEN) {
582 log.debug("Wrong length: Expected to be >= {}, was: {}",
583 MIN_EXPECTED_BYTE_LEN, bb.readableBytes());
584 return null;
585 }
586
587 byte ofVer = bb.readByte();
588
589 if (ofVer != ofVersion.wireVersion) {
590 log.debug("Wrong version: Expected={}, got={}",
591 ofVersion.wireVersion, ofVer);
592 return null;
593 }
594
595 byte type = bb.readByte();
596
597 if (type != OFType.FLOW_MOD.ordinal()) {
598 log.debug("Wrong type: Expected={}, got={}",
599 OFType.FLOW_MOD.ordinal(), type);
600 return null;
601 }
602
603 int length = U16.f(bb.readShort());
604
605 if (length < MIN_EXPECTED_BYTE_LEN) {
606 log.debug("Wrong length: Expected to be >= {}, was: {}",
607 MIN_EXPECTED_BYTE_LEN, length);
608 return null;
609 }
610
611 bb.skipBytes(SKIP_BYTES);
612 return U64.ofRaw(bb.readLong());
613 }
614
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700615 @Override
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700616 public void receivedRoleReply(Dpid dpid, RoleState requested,
Thomas Vachuska9b2da212014-11-10 19:30:25 -0800617 RoleState response) {
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700618 // Do nothing here for now.
619 }
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700620
Rodrigo Duarte Sousae37d1292017-08-29 17:01:24 -0300621 private DriverHandler getDriver(DeviceId devId) {
622 Driver driver = driverService.getDriver(devId);
623 DriverHandler handler = new DefaultDriverHandler(new DefaultDriverData(driver, devId));
624 return handler;
625 }
626
627 private void pushFlowMetrics(Dpid dpid, OFFlowStatsReply replies, DriverHandler handler) {
alshabib64def642014-12-02 23:27:37 -0800628
alshabib54ce5892014-09-23 17:50:51 -0700629 DeviceId did = DeviceId.deviceId(Dpid.uri(dpid));
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900630 NewAdaptiveFlowStatsCollector afsc = afsCollectors.get(dpid);
alshabib54ce5892014-09-23 17:50:51 -0700631
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900632 if (adaptiveFlowSampling && afsc != null) {
633 List<FlowEntry> flowEntries = replies.getEntries().stream()
Rodrigo Duarte Sousae37d1292017-08-29 17:01:24 -0300634 .map(entry -> new FlowEntryBuilder(did, entry, handler).withSetAfsc(afsc).build())
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900635 .collect(Collectors.toList());
alshabib54ce5892014-09-23 17:50:51 -0700636
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900637 // Check that OFFlowStatsReply Xid is same with the one of OFFlowStatsRequest?
638 if (afsc.getFlowMissingXid() != NewAdaptiveFlowStatsCollector.NO_FLOW_MISSING_XID) {
639 log.debug("OpenFlowRuleProvider:pushFlowMetrics, flowMissingXid={}, "
640 + "OFFlowStatsReply Xid={}, for {}",
641 afsc.getFlowMissingXid(), replies.getXid(), dpid);
642 if (afsc.getFlowMissingXid() == replies.getXid()) {
643 // call entire flow stats update with flowMissing synchronization.
644 // used existing pushFlowMetrics
645 providerService.pushFlowMetrics(did, flowEntries);
ssyoon9030fbcd92015-08-17 10:42:07 +0900646 }
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900647 // reset flowMissingXid to NO_FLOW_MISSING_XID
648 afsc.setFlowMissingXid(NewAdaptiveFlowStatsCollector.NO_FLOW_MISSING_XID);
649 } else {
650 // call individual flow stats update
651 providerService.pushFlowMetricsWithoutFlowMissing(did, flowEntries);
ssyoon9030fbcd92015-08-17 10:42:07 +0900652 }
653 } else {
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900654 List<FlowEntry> flowEntries = replies.getEntries().stream()
Rodrigo Duarte Sousae37d1292017-08-29 17:01:24 -0300655 .map(entry -> new FlowEntryBuilder(did, entry, handler).build())
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900656 .collect(Collectors.toList());
657
ssyoon9030fbcd92015-08-17 10:42:07 +0900658 // call existing entire flow stats update with flowMissing synchronization
659 providerService.pushFlowMetrics(did, flowEntries);
660 }
alshabib5c370ff2014-09-18 10:12:14 -0700661 }
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700662
663 private void pushTableStatistics(Dpid dpid, OFTableStatsReply replies) {
664
665 DeviceId did = DeviceId.deviceId(Dpid.uri(dpid));
666 List<TableStatisticsEntry> tableStatsEntries = replies.getEntries().stream()
667 .map(entry -> buildTableStatistics(did, entry))
668 .filter(Objects::nonNull)
669 .collect(Collectors.toList());
670 providerService.pushTableStatistics(did, tableStatsEntries);
671 }
672
Cem Türker3baff672017-10-12 15:09:01 +0300673 private void pushFlowLightWeightMetrics(Dpid dpid, OFFlowLightweightStatsReply replies) {
674
675 DeviceId did = DeviceId.deviceId(Dpid.uri(dpid));
676 NewAdaptiveFlowStatsCollector afsc = afsCollectors.get(dpid);
677 if (adaptiveFlowSampling && afsc != null) {
678 List<FlowEntry> flowEntries = replies.getEntries().stream()
679 .map(entry -> new FlowEntryBuilder(did, entry, driverService).withSetAfsc(afsc).build())
680 .collect(Collectors.toList());
681
682 // Check that OFFlowStatsReply Xid is same with the one of OFFlowStatsRequest?
683 if (afsc.getFlowMissingXid() != NewAdaptiveFlowStatsCollector.NO_FLOW_MISSING_XID) {
684 log.debug("OpenFlowRuleProvider:pushFlowMetrics, flowMissingXid={}, "
685 + "OFFlowStatsReply Xid={}, for {}",
686 afsc.getFlowMissingXid(), replies.getXid(), dpid);
687 if (afsc.getFlowMissingXid() == replies.getXid()) {
688 // call entire flow stats update with flowMissing synchronization.
689 // used existing pushFlowMetrics
690 providerService.pushFlowMetrics(did, flowEntries);
691 }
692 // reset flowMissingXid to NO_FLOW_MISSING_XID
693 afsc.setFlowMissingXid(NewAdaptiveFlowStatsCollector.NO_FLOW_MISSING_XID);
694 } else {
695 // call individual flow stats update
696 providerService.pushFlowMetricsWithoutFlowMissing(did, flowEntries);
697 }
698 } else {
699 List<FlowEntry> flowEntries = replies.getEntries().stream()
700 .map(entry -> new FlowEntryBuilder(did, entry, driverService).build())
701 .collect(Collectors.toList());
702 // call existing entire flow stats update with flowMissing synchronization
703 providerService.pushFlowMetrics(did, flowEntries);
704 }
705 }
706
Srikanth Vavilapalli95810f52015-09-14 15:49:56 -0700707 private TableStatisticsEntry buildTableStatistics(DeviceId deviceId,
708 OFTableStatsEntry ofEntry) {
709 TableStatisticsEntry entry = null;
710 if (ofEntry != null) {
711 entry = new DefaultTableStatisticsEntry(deviceId,
712 ofEntry.getTableId().getValue(),
713 ofEntry.getActiveCount(),
714 ofEntry.getLookupCount().getValue(),
715 ofEntry.getMatchedCount().getValue());
716 }
717
718 return entry;
719
720 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700721 }
alshabib1cc04f72014-09-16 16:09:58 -0700722
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800723 /**
jcc3d4e14a2015-04-21 11:32:05 +0800724 * The internal cache entry holding the original request as well as
725 * accumulating the any failures along the way.
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700726 * <p/>
jcc3d4e14a2015-04-21 11:32:05 +0800727 * If this entry is evicted from the cache then the entire operation is
728 * considered failed. Otherwise, only the failures reported by the device
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800729 * will be propagated up.
730 */
731 private class InternalCacheEntry {
alshabib902d41b2014-10-07 16:52:05 -0700732
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800733 private final FlowRuleBatchOperation operation;
734 private final Set<FlowRule> failures = Sets.newConcurrentHashSet();
alshabib193525b2014-10-08 18:58:03 -0700735
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800736 public InternalCacheEntry(FlowRuleBatchOperation operation) {
737 this.operation = operation;
alshabib902d41b2014-10-07 16:52:05 -0700738 }
739
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800740 /**
741 * Appends a failed rule to the set of failed items.
jcc3d4e14a2015-04-21 11:32:05 +0800742 *
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800743 * @param rule the failed rule
744 */
745 public void appendFailure(FlowRule rule) {
746 failures.add(rule);
Thomas Vachuska9b2da212014-11-10 19:30:25 -0800747 }
748
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800749 /**
750 * Fails the entire batch and returns the failed operation.
jcc3d4e14a2015-04-21 11:32:05 +0800751 *
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800752 * @return the failed operation
753 */
754 public CompletedBatchOperation failedCompletion() {
755 Set<FlowRule> fails = operation.getOperations().stream()
756 .map(op -> op.target()).collect(Collectors.toSet());
jcc3d4e14a2015-04-21 11:32:05 +0800757 return new CompletedBatchOperation(false,
758 Collections
759 .unmodifiableSet(fails),
760 operation.deviceId());
alshabib902d41b2014-10-07 16:52:05 -0700761 }
762
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800763 /**
764 * Returns the completed operation and whether the batch suceeded.
jcc3d4e14a2015-04-21 11:32:05 +0800765 *
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800766 * @return the completed operation
767 */
768 public CompletedBatchOperation completed() {
jcc3d4e14a2015-04-21 11:32:05 +0800769 return new CompletedBatchOperation(
Thomas Vachuska75aaa672015-04-29 12:24:43 -0700770 failures.isEmpty(),
771 Collections
772 .unmodifiableSet(failures),
773 operation.deviceId());
alshabib902d41b2014-10-07 16:52:05 -0700774 }
alshabib902d41b2014-10-07 16:52:05 -0700775 }
alshabiba68eb962014-09-24 20:34:13 -0700776
alshabib1cc04f72014-09-16 16:09:58 -0700777}