blob: 928f7b0407df1942d0b09c0876c8d4a61409ee65 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07002 * 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
Jonathan Hart2ffcd102015-01-16 16:47:50 -080018
Brian O'Connor72cb19a2015-01-16 16:14:41 -080019import com.google.common.cache.Cache;
20import com.google.common.cache.CacheBuilder;
21import com.google.common.cache.RemovalCause;
22import com.google.common.cache.RemovalNotification;
23import com.google.common.collect.Maps;
24import com.google.common.collect.Sets;
alshabib1cc04f72014-09-16 16:09:58 -070025import org.apache.felix.scr.annotations.Activate;
26import org.apache.felix.scr.annotations.Component;
27import org.apache.felix.scr.annotations.Deactivate;
28import org.apache.felix.scr.annotations.Reference;
29import org.apache.felix.scr.annotations.ReferenceCardinality;
Brian O'Connorabafb502014-12-02 22:26:20 -080030import org.onosproject.core.ApplicationId;
31import org.onosproject.net.DeviceId;
Brian O'Connorabafb502014-12-02 22:26:20 -080032import org.onosproject.net.flow.CompletedBatchOperation;
Brian O'Connorabafb502014-12-02 22:26:20 -080033import org.onosproject.net.flow.FlowEntry;
34import org.onosproject.net.flow.FlowRule;
35import org.onosproject.net.flow.FlowRuleBatchEntry;
Brian O'Connor72cb19a2015-01-16 16:14:41 -080036import org.onosproject.net.flow.FlowRuleBatchOperation;
Brian O'Connorabafb502014-12-02 22:26:20 -080037import org.onosproject.net.flow.FlowRuleProvider;
38import org.onosproject.net.flow.FlowRuleProviderRegistry;
39import org.onosproject.net.flow.FlowRuleProviderService;
40import org.onosproject.net.provider.AbstractProvider;
41import org.onosproject.net.provider.ProviderId;
Brian O'Connorabafb502014-12-02 22:26:20 -080042import org.onosproject.openflow.controller.Dpid;
43import org.onosproject.openflow.controller.OpenFlowController;
44import org.onosproject.openflow.controller.OpenFlowEventListener;
45import org.onosproject.openflow.controller.OpenFlowSwitch;
46import org.onosproject.openflow.controller.OpenFlowSwitchListener;
47import org.onosproject.openflow.controller.RoleState;
alshabib19fdc122014-10-03 11:38:19 -070048import org.projectfloodlight.openflow.protocol.OFActionType;
alshabib902d41b2014-10-07 16:52:05 -070049import org.projectfloodlight.openflow.protocol.OFBarrierRequest;
50import org.projectfloodlight.openflow.protocol.OFErrorMsg;
Brian O'Connor72cb19a2015-01-16 16:14:41 -080051import org.projectfloodlight.openflow.protocol.OFErrorType;
alshabib193525b2014-10-08 18:58:03 -070052import org.projectfloodlight.openflow.protocol.OFFlowMod;
alshabib8f1cf4a2014-09-17 14:44:48 -070053import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
alshabib5c370ff2014-09-18 10:12:14 -070054import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry;
55import org.projectfloodlight.openflow.protocol.OFFlowStatsReply;
alshabib19fdc122014-10-03 11:38:19 -070056import org.projectfloodlight.openflow.protocol.OFInstructionType;
alshabib8f1cf4a2014-09-17 14:44:48 -070057import org.projectfloodlight.openflow.protocol.OFMessage;
58import org.projectfloodlight.openflow.protocol.OFPortStatus;
alshabib5c370ff2014-09-18 10:12:14 -070059import org.projectfloodlight.openflow.protocol.OFStatsReply;
sangho89bf6fb2015-02-09 09:33:13 -080060import org.projectfloodlight.openflow.protocol.OFStatsType;
alshabib19fdc122014-10-03 11:38:19 -070061import org.projectfloodlight.openflow.protocol.OFVersion;
62import org.projectfloodlight.openflow.protocol.action.OFAction;
63import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
alshabib193525b2014-10-08 18:58:03 -070064import org.projectfloodlight.openflow.protocol.errormsg.OFFlowModFailedErrorMsg;
alshabib19fdc122014-10-03 11:38:19 -070065import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
66import org.projectfloodlight.openflow.protocol.instruction.OFInstructionApplyActions;
67import org.projectfloodlight.openflow.types.OFPort;
alshabib1cc04f72014-09-16 16:09:58 -070068import org.slf4j.Logger;
69
Brian O'Connor72cb19a2015-01-16 16:14:41 -080070import java.util.Collections;
71import java.util.List;
72import java.util.Map;
73import java.util.Optional;
74import java.util.Set;
75import java.util.concurrent.TimeUnit;
76import java.util.stream.Collectors;
77
78import static org.slf4j.LoggerFactory.getLogger;
79
alshabibeec3a062014-09-17 18:01:26 -070080
alshabib1cc04f72014-09-16 16:09:58 -070081/**
82 * Provider which uses an OpenFlow controller to detect network
83 * end-station hosts.
84 */
85@Component(immediate = true)
86public class OpenFlowRuleProvider extends AbstractProvider implements FlowRuleProvider {
87
Jonathan Hart2ffcd102015-01-16 16:47:50 -080088 private static final int LOWEST_PRIORITY = 0;
89
alshabib1cc04f72014-09-16 16:09:58 -070090 private final Logger log = getLogger(getClass());
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 protected FlowRuleProviderRegistry providerRegistry;
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 protected OpenFlowController controller;
97
alshabib1cc04f72014-09-16 16:09:58 -070098
99 private FlowRuleProviderService providerService;
100
alshabibeec3a062014-09-17 18:01:26 -0700101 private final InternalFlowProvider listener = new InternalFlowProvider();
102
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800103 private Cache<Long, InternalCacheEntry> pendingBatches;
alshabib193525b2014-10-08 18:58:03 -0700104
alshabib3d643ec2014-10-22 18:33:00 -0700105 private final Map<Dpid, FlowStatsCollector> collectors = Maps.newHashMap();
106
Thomas Vachuska9b2da212014-11-10 19:30:25 -0800107
alshabib1cc04f72014-09-16 16:09:58 -0700108 /**
109 * Creates an OpenFlow host provider.
110 */
111 public OpenFlowRuleProvider() {
Brian O'Connorabafb502014-12-02 22:26:20 -0800112 super(new ProviderId("of", "org.onosproject.provider.openflow"));
alshabib1cc04f72014-09-16 16:09:58 -0700113 }
114
115 @Activate
116 public void activate() {
117 providerService = providerRegistry.register(this);
alshabibeec3a062014-09-17 18:01:26 -0700118 controller.addListener(listener);
119 controller.addEventListener(listener);
alshabib3d643ec2014-10-22 18:33:00 -0700120
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800121 pendingBatches = CacheBuilder.newBuilder()
122 .expireAfterWrite(10, TimeUnit.SECONDS)
123 .removalListener((RemovalNotification<Long, InternalCacheEntry> notification) -> {
124 if (notification.getCause() == RemovalCause.EXPIRED) {
125 providerService.batchOperationCompleted(notification.getKey(),
126 notification.getValue().failedCompletion());
127 }
128 }).build();
129
130
alshabib3d643ec2014-10-22 18:33:00 -0700131 for (OpenFlowSwitch sw : controller.getSwitches()) {
132 FlowStatsCollector fsc = new FlowStatsCollector(sw, POLL_INTERVAL);
133 fsc.start();
134 collectors.put(new Dpid(sw.getId()), fsc);
135 }
136
137
alshabib1cc04f72014-09-16 16:09:58 -0700138 log.info("Started");
139 }
140
141 @Deactivate
142 public void deactivate() {
143 providerRegistry.unregister(this);
144 providerService = null;
145
146 log.info("Stopped");
147 }
Thomas Vachuska9b2da212014-11-10 19:30:25 -0800148
alshabib1cc04f72014-09-16 16:09:58 -0700149 @Override
150 public void applyFlowRule(FlowRule... flowRules) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800151 for (FlowRule flowRule : flowRules) {
152 applyRule(flowRule);
alshabib35edb1a2014-09-16 17:44:44 -0700153 }
alshabib1cc04f72014-09-16 16:09:58 -0700154 }
155
alshabib35edb1a2014-09-16 17:44:44 -0700156 private void applyRule(FlowRule flowRule) {
157 OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId().uri()));
sangho87af8112015-01-29 12:53:08 -0800158 if (flowRule.type() == FlowRule.Type.DEFAULT) {
159 sw.sendMsg(FlowModBuilder.builder(flowRule, sw.factory(),
160 Optional.empty()).buildFlowAdd());
161 } else {
alshabib9af70072015-02-09 14:34:16 -0800162 OpenFlowSwitch.TableType type = getTableType(flowRule.type());
sangho87af8112015-01-29 12:53:08 -0800163 sw.sendMsg(FlowModBuilder.builder(flowRule, sw.factory(),
alshabib9af70072015-02-09 14:34:16 -0800164 Optional.empty()).buildFlowAdd(),
165 type);
sangho87af8112015-01-29 12:53:08 -0800166 }
alshabib35edb1a2014-09-16 17:44:44 -0700167 }
168
alshabib35edb1a2014-09-16 17:44:44 -0700169
alshabib1cc04f72014-09-16 16:09:58 -0700170 @Override
171 public void removeFlowRule(FlowRule... flowRules) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800172 for (FlowRule flowRule : flowRules) {
173 removeRule(flowRule);
alshabib219ebaa2014-09-22 15:41:24 -0700174 }
alshabib1cc04f72014-09-16 16:09:58 -0700175
176 }
177
alshabib219ebaa2014-09-22 15:41:24 -0700178 private void removeRule(FlowRule flowRule) {
179 OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId().uri()));
sangho87af8112015-01-29 12:53:08 -0800180 if (flowRule.type() == FlowRule.Type.DEFAULT) {
181 sw.sendMsg(FlowModBuilder.builder(flowRule, sw.factory(),
182 Optional.empty()).buildFlowDel());
183 } else {
184 sw.sendMsg(FlowModBuilder.builder(flowRule, sw.factory(),
185 Optional.empty()).buildFlowDel(), getTableType(flowRule.type()));
186 }
alshabib219ebaa2014-09-22 15:41:24 -0700187 }
188
alshabiba68eb962014-09-24 20:34:13 -0700189 @Override
190 public void removeRulesById(ApplicationId id, FlowRule... flowRules) {
191 // TODO: optimize using the ApplicationId
192 removeFlowRule(flowRules);
193 }
194
alshabib193525b2014-10-08 18:58:03 -0700195 @Override
sangho87af8112015-01-29 12:53:08 -0800196
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800197 public void executeBatch(FlowRuleBatchOperation batch) {
198
199 pendingBatches.put(batch.id(), new InternalCacheEntry(batch));
200
201
202 OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(batch.deviceId().uri()));
203 OFFlowMod mod;
204
alshabib193525b2014-10-08 18:58:03 -0700205 for (FlowRuleBatchEntry fbe : batch.getOperations()) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800206
Brian O'Connor427a1762014-11-19 18:40:32 -0800207 FlowModBuilder builder =
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800208 FlowModBuilder.builder(fbe.target(), sw.factory(),
209 Optional.of(batch.id()));
Sho SHIMIZUaba9d002015-01-29 14:51:04 -0800210 switch (fbe.operator()) {
alshabib193525b2014-10-08 18:58:03 -0700211 case ADD:
212 mod = builder.buildFlowAdd();
213 break;
214 case REMOVE:
215 mod = builder.buildFlowDel();
216 break;
217 case MODIFY:
218 mod = builder.buildFlowMod();
219 break;
220 default:
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800221 log.error("Unsupported batch operation {}; skipping flowmod {}",
222 fbe.operator(), fbe);
223 continue;
224 }
sangho22a805d2015-02-13 09:45:43 -0800225 if (fbe.target().type() == FlowRule.Type.DEFAULT) {
226 sw.sendMsg(mod);
227 } else {
228 sw.sendMsg(mod, getTableType(fbe.target().type()));
229 }
alshabib193525b2014-10-08 18:58:03 -0700230 }
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800231 OFBarrierRequest.Builder builder = sw.factory()
232 .buildBarrierRequest()
233 .setXid(batch.id());
234 sw.sendMsg(builder.build());
alshabib193525b2014-10-08 18:58:03 -0700235 }
236
sangho87af8112015-01-29 12:53:08 -0800237 private OpenFlowSwitch.TableType getTableType(FlowRule.Type type) {
238 switch (type) {
alshabib9af70072015-02-09 14:34:16 -0800239
240 case DEFAULT:
241 return OpenFlowSwitch.TableType.NONE;
sangho87af8112015-01-29 12:53:08 -0800242 case IP:
243 return OpenFlowSwitch.TableType.IP;
244 case MPLS:
245 return OpenFlowSwitch.TableType.MPLS;
246 case ACL:
247 return OpenFlowSwitch.TableType.ACL;
alshabib9af70072015-02-09 14:34:16 -0800248 case VLAN_MPLS:
249 return OpenFlowSwitch.TableType.VLAN_MPLS;
250 case VLAN:
251 return OpenFlowSwitch.TableType.VLAN;
252 case ETHER:
253 return OpenFlowSwitch.TableType.ETHER;
254 case COS:
255 return OpenFlowSwitch.TableType.COS;
sangho87af8112015-01-29 12:53:08 -0800256 default:
257 return OpenFlowSwitch.TableType.NONE;
258 }
259 }
alshabib193525b2014-10-08 18:58:03 -0700260
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800261
262
263
alshabib8f1cf4a2014-09-17 14:44:48 -0700264 private class InternalFlowProvider
Thomas Vachuska9b2da212014-11-10 19:30:25 -0800265 implements OpenFlowSwitchListener, OpenFlowEventListener {
alshabib8f1cf4a2014-09-17 14:44:48 -0700266
alshabib8f1cf4a2014-09-17 14:44:48 -0700267 @Override
268 public void switchAdded(Dpid dpid) {
alshabibba5ac482014-10-02 17:15:20 -0700269 FlowStatsCollector fsc = new FlowStatsCollector(controller.getSwitch(dpid), POLL_INTERVAL);
alshabibeec3a062014-09-17 18:01:26 -0700270 fsc.start();
271 collectors.put(dpid, fsc);
alshabib8f1cf4a2014-09-17 14:44:48 -0700272 }
273
274 @Override
275 public void switchRemoved(Dpid dpid) {
alshabibdfc7afb2014-10-21 20:13:27 -0700276 FlowStatsCollector collector = collectors.remove(dpid);
277 if (collector != null) {
278 collector.stop();
279 }
alshabib8f1cf4a2014-09-17 14:44:48 -0700280 }
281
282 @Override
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700283 public void switchChanged(Dpid dpid) {
284 }
285
286 @Override
alshabib8f1cf4a2014-09-17 14:44:48 -0700287 public void portChanged(Dpid dpid, OFPortStatus status) {
288 //TODO: Decide whether to evict flows internal store.
289 }
290
291 @Override
292 public void handleMessage(Dpid dpid, OFMessage msg) {
293 switch (msg.getType()) {
Thomas Vachuska9b2da212014-11-10 19:30:25 -0800294 case FLOW_REMOVED:
295 OFFlowRemoved removed = (OFFlowRemoved) msg;
alshabib6b5cfec2014-09-18 17:42:18 -0700296
Thomas Vachuska9b2da212014-11-10 19:30:25 -0800297 FlowEntry fr = new FlowEntryBuilder(dpid, removed).build();
298 providerService.flowRemoved(fr);
299 break;
300 case STATS_REPLY:
sangho20f91162015-02-12 11:24:23 -0800301 if (((OFStatsReply) msg).getStatsType() == OFStatsType.FLOW) {
sangho89bf6fb2015-02-09 09:33:13 -0800302 pushFlowMetrics(dpid, (OFFlowStatsReply) msg);
303 }
Thomas Vachuska9b2da212014-11-10 19:30:25 -0800304 break;
305 case BARRIER_REPLY:
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800306 try {
307 InternalCacheEntry entry = pendingBatches.getIfPresent(msg.getXid());
308 if (entry != null) {
309 providerService.batchOperationCompleted(msg.getXid(), entry.completed());
310 } else {
311 log.warn("Received unknown Barrier Reply: {}", msg.getXid());
312 }
313 } finally {
314 pendingBatches.invalidate(msg.getXid());
Thomas Vachuska9b2da212014-11-10 19:30:25 -0800315 }
316 break;
317 case ERROR:
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800318 log.warn("received Error message {} from {}", msg, dpid);
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800319
320 OFErrorMsg error = (OFErrorMsg) msg;
321 if (error.getErrType() == OFErrorType.FLOW_MOD_FAILED) {
322 OFFlowModFailedErrorMsg fmFailed = (OFFlowModFailedErrorMsg) error;
323 if (fmFailed.getData().getParsedMessage().isPresent()) {
324 OFMessage m = fmFailed.getData().getParsedMessage().get();
325 OFFlowMod fm = (OFFlowMod) m;
326 InternalCacheEntry entry = pendingBatches.getIfPresent(msg.getXid());
327 if (entry != null) {
328 entry.appendFailure(new FlowEntryBuilder(dpid, fm).build());
329 } else {
330 log.error("No matching batch for this error: {}", error);
331 }
332 } else {
333 //FIXME: Potentially add flowtracking to avoid this message.
334 log.error("Flow installation failed but switch didn't" +
335 " tell us which one.");
336 }
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800337 } else {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800338 log.warn("Received error {}", error);
Thomas Vachuska9b2da212014-11-10 19:30:25 -0800339 }
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800340
341
Thomas Vachuska9b2da212014-11-10 19:30:25 -0800342 default:
343 log.debug("Unhandled message type: {}", msg.getType());
alshabib8f1cf4a2014-09-17 14:44:48 -0700344 }
345
346 }
347
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700348 @Override
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700349 public void receivedRoleReply(Dpid dpid, RoleState requested,
Thomas Vachuska9b2da212014-11-10 19:30:25 -0800350 RoleState response) {
Ayaka Koshibe3ef2b0d2014-10-31 13:58:27 -0700351 // Do nothing here for now.
352 }
Ayaka Koshibeab91cc42014-09-25 10:20:52 -0700353
sangho89bf6fb2015-02-09 09:33:13 -0800354 private void pushFlowMetrics(Dpid dpid, OFFlowStatsReply replies) {
alshabib64def642014-12-02 23:27:37 -0800355
alshabib54ce5892014-09-23 17:50:51 -0700356 DeviceId did = DeviceId.deviceId(Dpid.uri(dpid));
alshabib54ce5892014-09-23 17:50:51 -0700357
alshabib64def642014-12-02 23:27:37 -0800358 List<FlowEntry> flowEntries = replies.getEntries().stream()
359 .filter(entry -> !tableMissRule(dpid, entry))
360 .map(entry -> new FlowEntryBuilder(dpid, entry).build())
361 .collect(Collectors.toList());
alshabib54ce5892014-09-23 17:50:51 -0700362
alshabib64def642014-12-02 23:27:37 -0800363 providerService.pushFlowMetrics(did, flowEntries);
364
alshabib5c370ff2014-09-18 10:12:14 -0700365 }
366
alshabib19fdc122014-10-03 11:38:19 -0700367 private boolean tableMissRule(Dpid dpid, OFFlowStatsEntry reply) {
Jonathan Hart2ffcd102015-01-16 16:47:50 -0800368 if (reply.getMatch().getMatchFields().iterator().hasNext()) {
alshabib19fdc122014-10-03 11:38:19 -0700369 return false;
370 }
Jonathan Hart2ffcd102015-01-16 16:47:50 -0800371 if (reply.getVersion().equals(OFVersion.OF_10)) {
372 return reply.getPriority() == LOWEST_PRIORITY
373 && reply.getActions().isEmpty();
374 }
alshabib19fdc122014-10-03 11:38:19 -0700375 for (OFInstruction ins : reply.getInstructions()) {
376 if (ins.getType() == OFInstructionType.APPLY_ACTIONS) {
377 OFInstructionApplyActions apply = (OFInstructionApplyActions) ins;
378 List<OFAction> acts = apply.getActions();
379 for (OFAction act : acts) {
380 if (act.getType() == OFActionType.OUTPUT) {
381 OFActionOutput out = (OFActionOutput) act;
382 if (out.getPort() == OFPort.CONTROLLER) {
383 return true;
384 }
385 }
386 }
387 }
388 }
389 return false;
390 }
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700391
alshabib8f1cf4a2014-09-17 14:44:48 -0700392 }
alshabib1cc04f72014-09-16 16:09:58 -0700393
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800394 /**
395 * The internal cache entry holding the original request as well
396 * as accumulating the any failures along the way.
397 *
398 * If this entry is evicted from the cache then the entire operation
399 * is considered failed. Otherwise, only the failures reported by the device
400 * will be propagated up.
401 */
402 private class InternalCacheEntry {
alshabib902d41b2014-10-07 16:52:05 -0700403
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800404 private final FlowRuleBatchOperation operation;
405 private final Set<FlowRule> failures = Sets.newConcurrentHashSet();
alshabib193525b2014-10-08 18:58:03 -0700406
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800407 public InternalCacheEntry(FlowRuleBatchOperation operation) {
408 this.operation = operation;
alshabib902d41b2014-10-07 16:52:05 -0700409 }
410
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800411 /**
412 * Appends a failed rule to the set of failed items.
413 * @param rule the failed rule
414 */
415 public void appendFailure(FlowRule rule) {
416 failures.add(rule);
Thomas Vachuska9b2da212014-11-10 19:30:25 -0800417 }
418
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800419 /**
420 * Fails the entire batch and returns the failed operation.
421 * @return the failed operation
422 */
423 public CompletedBatchOperation failedCompletion() {
424 Set<FlowRule> fails = operation.getOperations().stream()
425 .map(op -> op.target()).collect(Collectors.toSet());
426 return new CompletedBatchOperation(false, Collections.unmodifiableSet(fails), operation.deviceId());
alshabib902d41b2014-10-07 16:52:05 -0700427 }
428
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800429 /**
430 * Returns the completed operation and whether the batch suceeded.
431 * @return the completed operation
432 */
433 public CompletedBatchOperation completed() {
434 return new CompletedBatchOperation(failures.isEmpty(),
435 Collections.unmodifiableSet(failures), operation.deviceId());
alshabib902d41b2014-10-07 16:52:05 -0700436 }
437
alshabib902d41b2014-10-07 16:52:05 -0700438 }
alshabiba68eb962014-09-24 20:34:13 -0700439
alshabib1cc04f72014-09-16 16:09:58 -0700440}