Madan Jampani

Towards a distributed flow rule store

1 package org.onlab.onos.net.flow; 1 package org.onlab.onos.net.flow;
2 2
3 -import java.util.List; 3 +import java.util.Set;
4 4
5 /** 5 /**
6 * Interface capturing the result of a batch operation. 6 * Interface capturing the result of a batch operation.
...@@ -15,9 +15,9 @@ public interface BatchOperationResult<T> { ...@@ -15,9 +15,9 @@ public interface BatchOperationResult<T> {
15 boolean isSuccess(); 15 boolean isSuccess();
16 16
17 /** 17 /**
18 - * Obtains a list of items which failed. 18 + * Obtains a set of items which failed.
19 - * @return a list of failures 19 + * @return a set of failures
20 */ 20 */
21 - List<T> failedItems(); 21 + Set<T> failedItems();
22 22
23 } 23 }
......
1 package org.onlab.onos.net.flow; 1 package org.onlab.onos.net.flow;
2 2
3 -import java.util.List; 3 +import java.util.Set;
4 4
5 -import com.google.common.collect.ImmutableList; 5 +import com.google.common.collect.ImmutableSet;
6 6
7 public class CompletedBatchOperation implements BatchOperationResult<FlowEntry> { 7 public class CompletedBatchOperation implements BatchOperationResult<FlowEntry> {
8 8
9 9
10 private final boolean success; 10 private final boolean success;
11 - private final List<FlowEntry> failures; 11 + private final Set<FlowEntry> failures;
12 12
13 - public CompletedBatchOperation(boolean success, List<FlowEntry> failures) { 13 + public CompletedBatchOperation(boolean success, Set<FlowEntry> failures) {
14 this.success = success; 14 this.success = success;
15 - this.failures = ImmutableList.copyOf(failures); 15 + this.failures = ImmutableSet.copyOf(failures);
16 } 16 }
17 17
18 @Override 18 @Override
...@@ -21,7 +21,7 @@ public class CompletedBatchOperation implements BatchOperationResult<FlowEntry> ...@@ -21,7 +21,7 @@ public class CompletedBatchOperation implements BatchOperationResult<FlowEntry>
21 } 21 }
22 22
23 @Override 23 @Override
24 - public List<FlowEntry> failedItems() { 24 + public Set<FlowEntry> failedItems() {
25 return failures; 25 return failures;
26 } 26 }
27 27
......
1 +package org.onlab.onos.net.flow;
2 +
3 +import org.onlab.onos.event.AbstractEvent;
4 +
5 +/**
6 + * Describes flow rule batch event.
7 + */
8 +public final class FlowRuleBatchEvent extends AbstractEvent<FlowRuleBatchEvent.Type, FlowRuleBatchRequest> {
9 +
10 + /**
11 + * Type of flow rule events.
12 + */
13 + public enum Type {
14 +
15 + /**
16 + * Signifies that a batch operation has been initiated.
17 + */
18 + BATCH_OPERATION_REQUESTED,
19 +
20 + /**
21 + * Signifies that a batch operation has completed.
22 + */
23 + BATCH_OPERATION_COMPLETED,
24 + }
25 +
26 + private final CompletedBatchOperation result;
27 +
28 + /**
29 + * Constructs a new FlowRuleBatchEvent.
30 + * @param request batch operation request.
31 + * @return event.
32 + */
33 + public static FlowRuleBatchEvent create(FlowRuleBatchRequest request) {
34 + FlowRuleBatchEvent event = new FlowRuleBatchEvent(Type.BATCH_OPERATION_REQUESTED, request, null);
35 + return event;
36 + }
37 +
38 + /**
39 + * Constructs a new FlowRuleBatchEvent.
40 + * @param request batch operation request.
41 + * @param result completed batch operation result.
42 + * @return event.
43 + */
44 + public static FlowRuleBatchEvent create(FlowRuleBatchRequest request, CompletedBatchOperation result) {
45 + FlowRuleBatchEvent event = new FlowRuleBatchEvent(Type.BATCH_OPERATION_COMPLETED, request, result);
46 + return event;
47 + }
48 +
49 + /**
50 + * Returns the result of this batch operation.
51 + * @return batch operation result.
52 + */
53 + public CompletedBatchOperation result() {
54 + return result;
55 + }
56 +
57 + /**
58 + * Creates an event of a given type and for the specified flow rule batch.
59 + *
60 + * @param type flow rule batch event type
61 + * @param batch event flow rule batch subject
62 + */
63 + private FlowRuleBatchEvent(Type type, FlowRuleBatchRequest request, CompletedBatchOperation result) {
64 + super(type, request);
65 + this.result = result;
66 + }
67 +}
1 +package org.onlab.onos.net.flow;
2 +
3 +import java.util.Collections;
4 +import java.util.List;
5 +
6 +import org.onlab.onos.net.flow.FlowRuleBatchEntry.FlowRuleOperation;
7 +
8 +import com.google.common.collect.Lists;
9 +
10 +public class FlowRuleBatchRequest {
11 +
12 + private final List<FlowEntry> toAdd;
13 + private final List<FlowEntry> toRemove;
14 +
15 + public FlowRuleBatchRequest(List<FlowEntry> toAdd, List<FlowEntry> toRemove) {
16 + this.toAdd = Collections.unmodifiableList(toAdd);
17 + this.toRemove = Collections.unmodifiableList(toRemove);
18 + }
19 +
20 + public List<FlowEntry> toAdd() {
21 + return toAdd;
22 + }
23 +
24 + public List<FlowEntry> toRemove() {
25 + return toRemove;
26 + }
27 +
28 + public FlowRuleBatchOperation asBatchOperation() {
29 + List<FlowRuleBatchEntry> entries = Lists.newArrayList();
30 + for (FlowEntry e : toAdd) {
31 + entries.add(new FlowRuleBatchEntry(FlowRuleOperation.ADD, e));
32 + }
33 + for (FlowEntry e : toRemove) {
34 + entries.add(new FlowRuleBatchEntry(FlowRuleOperation.REMOVE, e));
35 + }
36 + return new FlowRuleBatchOperation(entries);
37 + }
38 +}
1 package org.onlab.onos.net.flow; 1 package org.onlab.onos.net.flow;
2 2
3 -import java.util.concurrent.Future;
4 -
5 import org.onlab.onos.ApplicationId; 3 import org.onlab.onos.ApplicationId;
6 import org.onlab.onos.net.intent.BatchOperation; 4 import org.onlab.onos.net.intent.BatchOperation;
7 import org.onlab.onos.net.provider.Provider; 5 import org.onlab.onos.net.provider.Provider;
8 6
7 +import com.google.common.util.concurrent.ListenableFuture;
8 +
9 /** 9 /**
10 * Abstraction of a flow rule provider. 10 * Abstraction of a flow rule provider.
11 */ 11 */
...@@ -43,6 +43,6 @@ public interface FlowRuleProvider extends Provider { ...@@ -43,6 +43,6 @@ public interface FlowRuleProvider extends Provider {
43 * @param batch a batch of flow rules 43 * @param batch a batch of flow rules
44 * @return a future indicating the status of this execution 44 * @return a future indicating the status of this execution
45 */ 45 */
46 - Future<CompletedBatchOperation> executeBatch(BatchOperation<FlowRuleBatchEntry> batch); 46 + ListenableFuture<CompletedBatchOperation> executeBatch(BatchOperation<FlowRuleBatchEntry> batch);
47 47
48 } 48 }
......
1 package org.onlab.onos.net.flow; 1 package org.onlab.onos.net.flow;
2 2
3 +import java.util.concurrent.Future;
4 +
3 import org.onlab.onos.ApplicationId; 5 import org.onlab.onos.ApplicationId;
4 import org.onlab.onos.net.DeviceId; 6 import org.onlab.onos.net.DeviceId;
5 import org.onlab.onos.store.Store; 7 import org.onlab.onos.store.Store;
...@@ -7,7 +9,7 @@ import org.onlab.onos.store.Store; ...@@ -7,7 +9,7 @@ import org.onlab.onos.store.Store;
7 /** 9 /**
8 * Manages inventory of flow rules; not intended for direct use. 10 * Manages inventory of flow rules; not intended for direct use.
9 */ 11 */
10 -public interface FlowRuleStore extends Store<FlowRuleEvent, FlowRuleStoreDelegate> { 12 +public interface FlowRuleStore extends Store<FlowRuleBatchEvent, FlowRuleStoreDelegate> {
11 13
12 /** 14 /**
13 * Returns the number of flow rule in the store. 15 * Returns the number of flow rule in the store.
...@@ -41,12 +43,26 @@ public interface FlowRuleStore extends Store<FlowRuleEvent, FlowRuleStoreDelegat ...@@ -41,12 +43,26 @@ public interface FlowRuleStore extends Store<FlowRuleEvent, FlowRuleStoreDelegat
41 Iterable<FlowRule> getFlowRulesByAppId(ApplicationId appId); 43 Iterable<FlowRule> getFlowRulesByAppId(ApplicationId appId);
42 44
43 /** 45 /**
46 + // TODO: Better description of method behavior.
44 * Stores a new flow rule without generating events. 47 * Stores a new flow rule without generating events.
45 * 48 *
46 * @param rule the flow rule to add 49 * @param rule the flow rule to add
47 - * @return true if the rule should be handled locally
48 */ 50 */
49 - boolean storeFlowRule(FlowRule rule); 51 + void storeFlowRule(FlowRule rule);
52 +
53 + /**
54 + * Stores a batch of flow rules.
55 + * @param batchOperation batch of flow rules.
56 + * @return Future response indicating success/failure of the batch operation
57 + * all the way down to the device.
58 + */
59 + Future<CompletedBatchOperation> storeBatch(FlowRuleBatchOperation batchOperation);
60 +
61 + /**
62 + * Invoked on the completion of a storeBatch operation.
63 + * @param result
64 + */
65 + void batchOperationComplete(FlowRuleBatchEvent event);
50 66
51 /** 67 /**
52 * Marks a flow rule for deletion. Actual deletion will occur 68 * Marks a flow rule for deletion. Actual deletion will occur
...@@ -55,7 +71,7 @@ public interface FlowRuleStore extends Store<FlowRuleEvent, FlowRuleStoreDelegat ...@@ -55,7 +71,7 @@ public interface FlowRuleStore extends Store<FlowRuleEvent, FlowRuleStoreDelegat
55 * @param rule the flow rule to delete 71 * @param rule the flow rule to delete
56 * @return true if the rule should be handled locally 72 * @return true if the rule should be handled locally
57 */ 73 */
58 - boolean deleteFlowRule(FlowRule rule); 74 + void deleteFlowRule(FlowRule rule);
59 75
60 /** 76 /**
61 * Stores a new flow rule, or updates an existing entry. 77 * Stores a new flow rule, or updates an existing entry.
......
...@@ -5,5 +5,5 @@ import org.onlab.onos.store.StoreDelegate; ...@@ -5,5 +5,5 @@ import org.onlab.onos.store.StoreDelegate;
5 /** 5 /**
6 * Flow rule store delegate abstraction. 6 * Flow rule store delegate abstraction.
7 */ 7 */
8 -public interface FlowRuleStoreDelegate extends StoreDelegate<FlowRuleEvent> { 8 +public interface FlowRuleStoreDelegate extends StoreDelegate<FlowRuleBatchEvent> {
9 } 9 }
......
...@@ -5,8 +5,10 @@ import static org.slf4j.LoggerFactory.getLogger; ...@@ -5,8 +5,10 @@ import static org.slf4j.LoggerFactory.getLogger;
5 5
6 import java.util.List; 6 import java.util.List;
7 import java.util.Map; 7 import java.util.Map;
8 +import java.util.Set;
8 import java.util.concurrent.CancellationException; 9 import java.util.concurrent.CancellationException;
9 import java.util.concurrent.ExecutionException; 10 import java.util.concurrent.ExecutionException;
11 +import java.util.concurrent.Executors;
10 import java.util.concurrent.Future; 12 import java.util.concurrent.Future;
11 import java.util.concurrent.TimeUnit; 13 import java.util.concurrent.TimeUnit;
12 import java.util.concurrent.TimeoutException; 14 import java.util.concurrent.TimeoutException;
...@@ -30,7 +32,9 @@ import org.onlab.onos.net.flow.FlowEntry; ...@@ -30,7 +32,9 @@ import org.onlab.onos.net.flow.FlowEntry;
30 import org.onlab.onos.net.flow.FlowRule; 32 import org.onlab.onos.net.flow.FlowRule;
31 import org.onlab.onos.net.flow.FlowRuleBatchEntry; 33 import org.onlab.onos.net.flow.FlowRuleBatchEntry;
32 import org.onlab.onos.net.flow.FlowRuleBatchEntry.FlowRuleOperation; 34 import org.onlab.onos.net.flow.FlowRuleBatchEntry.FlowRuleOperation;
35 +import org.onlab.onos.net.flow.FlowRuleBatchEvent;
33 import org.onlab.onos.net.flow.FlowRuleBatchOperation; 36 import org.onlab.onos.net.flow.FlowRuleBatchOperation;
37 +import org.onlab.onos.net.flow.FlowRuleBatchRequest;
34 import org.onlab.onos.net.flow.FlowRuleEvent; 38 import org.onlab.onos.net.flow.FlowRuleEvent;
35 import org.onlab.onos.net.flow.FlowRuleListener; 39 import org.onlab.onos.net.flow.FlowRuleListener;
36 import org.onlab.onos.net.flow.FlowRuleProvider; 40 import org.onlab.onos.net.flow.FlowRuleProvider;
...@@ -47,6 +51,9 @@ import com.google.common.collect.ArrayListMultimap; ...@@ -47,6 +51,9 @@ import com.google.common.collect.ArrayListMultimap;
47 import com.google.common.collect.Lists; 51 import com.google.common.collect.Lists;
48 import com.google.common.collect.Maps; 52 import com.google.common.collect.Maps;
49 import com.google.common.collect.Multimap; 53 import com.google.common.collect.Multimap;
54 +import com.google.common.collect.Sets;
55 +import com.google.common.util.concurrent.Futures;
56 +import com.google.common.util.concurrent.ListenableFuture;
50 57
51 /** 58 /**
52 * Provides implementation of the flow NB &amp; SB APIs. 59 * Provides implementation of the flow NB &amp; SB APIs.
...@@ -104,11 +111,7 @@ public class FlowRuleManager ...@@ -104,11 +111,7 @@ public class FlowRuleManager
104 public void applyFlowRules(FlowRule... flowRules) { 111 public void applyFlowRules(FlowRule... flowRules) {
105 for (int i = 0; i < flowRules.length; i++) { 112 for (int i = 0; i < flowRules.length; i++) {
106 FlowRule f = flowRules[i]; 113 FlowRule f = flowRules[i];
107 - boolean local = store.storeFlowRule(f); 114 + store.storeFlowRule(f);
108 - if (local) {
109 - // TODO: aggregate all local rules and push down once?
110 - applyFlowRulesToProviders(f);
111 - }
112 } 115 }
113 } 116 }
114 117
...@@ -132,11 +135,7 @@ public class FlowRuleManager ...@@ -132,11 +135,7 @@ public class FlowRuleManager
132 FlowRule f; 135 FlowRule f;
133 for (int i = 0; i < flowRules.length; i++) { 136 for (int i = 0; i < flowRules.length; i++) {
134 f = flowRules[i]; 137 f = flowRules[i];
135 - boolean local = store.deleteFlowRule(f); 138 + store.deleteFlowRule(f);
136 - if (local) {
137 - // TODO: aggregate all local rules and push down once?
138 - removeFlowRulesFromProviders(f);
139 - }
140 } 139 }
141 } 140 }
142 141
...@@ -180,33 +179,21 @@ public class FlowRuleManager ...@@ -180,33 +179,21 @@ public class FlowRuleManager
180 @Override 179 @Override
181 public Future<CompletedBatchOperation> applyBatch( 180 public Future<CompletedBatchOperation> applyBatch(
182 FlowRuleBatchOperation batch) { 181 FlowRuleBatchOperation batch) {
183 - Multimap<FlowRuleProvider, FlowRuleBatchEntry> batches = 182 + Multimap<DeviceId, FlowRuleBatchEntry> perDeviceBatches =
184 ArrayListMultimap.create(); 183 ArrayListMultimap.create();
185 List<Future<CompletedBatchOperation>> futures = Lists.newArrayList(); 184 List<Future<CompletedBatchOperation>> futures = Lists.newArrayList();
186 for (FlowRuleBatchEntry fbe : batch.getOperations()) { 185 for (FlowRuleBatchEntry fbe : batch.getOperations()) {
187 final FlowRule f = fbe.getTarget(); 186 final FlowRule f = fbe.getTarget();
188 - final Device device = deviceService.getDevice(f.deviceId()); 187 + perDeviceBatches.put(f.deviceId(), fbe);
189 - final FlowRuleProvider frp = getProvider(device.providerId());
190 - batches.put(frp, fbe);
191 - switch (fbe.getOperator()) {
192 - case ADD:
193 - store.storeFlowRule(f);
194 - break;
195 - case REMOVE:
196 - store.deleteFlowRule(f);
197 - break;
198 - case MODIFY:
199 - default:
200 - log.error("Batch operation type {} unsupported.", fbe.getOperator());
201 - }
202 } 188 }
203 - for (FlowRuleProvider provider : batches.keySet()) { 189 +
190 + for (DeviceId deviceId : perDeviceBatches.keySet()) {
204 FlowRuleBatchOperation b = 191 FlowRuleBatchOperation b =
205 - new FlowRuleBatchOperation(batches.get(provider)); 192 + new FlowRuleBatchOperation(perDeviceBatches.get(deviceId));
206 - Future<CompletedBatchOperation> future = provider.executeBatch(b); 193 + Future<CompletedBatchOperation> future = store.storeBatch(b);
207 futures.add(future); 194 futures.add(future);
208 } 195 }
209 - return new FlowRuleBatchFuture(futures, batches); 196 + return new FlowRuleBatchFuture(futures, perDeviceBatches);
210 } 197 }
211 198
212 @Override 199 @Override
...@@ -318,6 +305,7 @@ public class FlowRuleManager ...@@ -318,6 +305,7 @@ public class FlowRuleManager
318 post(event); 305 post(event);
319 } 306 }
320 } else { 307 } else {
308 + log.info("Removing flow rules....");
321 removeFlowRules(flowEntry); 309 removeFlowRules(flowEntry);
322 } 310 }
323 311
...@@ -385,21 +373,47 @@ public class FlowRuleManager ...@@ -385,21 +373,47 @@ public class FlowRuleManager
385 373
386 // Store delegate to re-post events emitted from the store. 374 // Store delegate to re-post events emitted from the store.
387 private class InternalStoreDelegate implements FlowRuleStoreDelegate { 375 private class InternalStoreDelegate implements FlowRuleStoreDelegate {
376 + // TODO: Right now we only dispatch events at individual flowEntry level.
377 + // It may be more efficient for also dispatch events as a batch.
388 @Override 378 @Override
389 - public void notify(FlowRuleEvent event) { 379 + public void notify(FlowRuleBatchEvent event) {
380 + final FlowRuleBatchRequest request = event.subject();
390 switch (event.type()) { 381 switch (event.type()) {
391 - case RULE_ADD_REQUESTED: 382 + case BATCH_OPERATION_REQUESTED:
392 - applyFlowRulesToProviders(event.subject()); 383 +// for (FlowEntry entry : request.toAdd()) {
393 - break; 384 +// //eventDispatcher.post(new FlowRuleEvent(FlowRuleEvent.Type.RULE_ADD_REQUESTED, entry));
394 - case RULE_REMOVE_REQUESTED: 385 +// }
395 - removeFlowRulesFromProviders(event.subject()); 386 +// for (FlowEntry entry : request.toRemove()) {
396 - break; 387 +// //eventDispatcher.post(new FlowRuleEvent(FlowRuleEvent.Type.RULE_REMOVE_REQUESTED, entry));
388 +// }
389 +// // FIXME: what about op.equals(FlowRuleOperation.MODIFY) ?
390 +//
391 + FlowRuleBatchOperation batchOperation = request.asBatchOperation();
392 +
393 + FlowRuleProvider flowRuleProvider =
394 + getProvider(batchOperation.getOperations().get(0).getTarget().deviceId());
395 + final ListenableFuture<CompletedBatchOperation> result =
396 + flowRuleProvider.executeBatch(batchOperation);
397 + result.addListener(new Runnable() {
398 + @Override
399 + public void run() {
400 + store.batchOperationComplete(FlowRuleBatchEvent.create(request, Futures.getUnchecked(result)));
401 + }
402 + }, Executors.newCachedThreadPool());
397 403
398 - case RULE_ADDED: 404 + break;
399 - case RULE_REMOVED: 405 + case BATCH_OPERATION_COMPLETED:
400 - case RULE_UPDATED: 406 + Set<FlowEntry> failedItems = event.result().failedItems();
401 - // only dispatch events related to switch 407 + for (FlowEntry entry : request.toAdd()) {
402 - eventDispatcher.post(event); 408 + if (!failedItems.contains(entry)) {
409 + eventDispatcher.post(new FlowRuleEvent(FlowRuleEvent.Type.RULE_ADDED, entry));
410 + }
411 + }
412 + for (FlowEntry entry : request.toRemove()) {
413 + if (!failedItems.contains(entry)) {
414 + eventDispatcher.post(new FlowRuleEvent(FlowRuleEvent.Type.RULE_REMOVED, entry));
415 + }
416 + }
403 break; 417 break;
404 default: 418 default:
405 break; 419 break;
...@@ -407,18 +421,15 @@ public class FlowRuleManager ...@@ -407,18 +421,15 @@ public class FlowRuleManager
407 } 421 }
408 } 422 }
409 423
410 - private class FlowRuleBatchFuture 424 + private class FlowRuleBatchFuture implements Future<CompletedBatchOperation> {
411 - implements Future<CompletedBatchOperation> {
412 425
413 private final List<Future<CompletedBatchOperation>> futures; 426 private final List<Future<CompletedBatchOperation>> futures;
414 - private final Multimap<FlowRuleProvider, FlowRuleBatchEntry> batches; 427 + private final Multimap<DeviceId, FlowRuleBatchEntry> batches;
415 private final AtomicReference<BatchState> state; 428 private final AtomicReference<BatchState> state;
416 private CompletedBatchOperation overall; 429 private CompletedBatchOperation overall;
417 430
418 -
419 -
420 public FlowRuleBatchFuture(List<Future<CompletedBatchOperation>> futures, 431 public FlowRuleBatchFuture(List<Future<CompletedBatchOperation>> futures,
421 - Multimap<FlowRuleProvider, FlowRuleBatchEntry> batches) { 432 + Multimap<DeviceId, FlowRuleBatchEntry> batches) {
422 this.futures = futures; 433 this.futures = futures;
423 this.batches = batches; 434 this.batches = batches;
424 state = new AtomicReference<FlowRuleManager.BatchState>(); 435 state = new AtomicReference<FlowRuleManager.BatchState>();
...@@ -460,7 +471,7 @@ public class FlowRuleManager ...@@ -460,7 +471,7 @@ public class FlowRuleManager
460 } 471 }
461 472
462 boolean success = true; 473 boolean success = true;
463 - List<FlowEntry> failed = Lists.newLinkedList(); 474 + Set<FlowEntry> failed = Sets.newHashSet();
464 CompletedBatchOperation completed; 475 CompletedBatchOperation completed;
465 for (Future<CompletedBatchOperation> future : futures) { 476 for (Future<CompletedBatchOperation> future : futures) {
466 completed = future.get(); 477 completed = future.get();
...@@ -480,7 +491,7 @@ public class FlowRuleManager ...@@ -480,7 +491,7 @@ public class FlowRuleManager
480 return overall; 491 return overall;
481 } 492 }
482 boolean success = true; 493 boolean success = true;
483 - List<FlowEntry> failed = Lists.newLinkedList(); 494 + Set<FlowEntry> failed = Sets.newHashSet();
484 CompletedBatchOperation completed; 495 CompletedBatchOperation completed;
485 long start = System.nanoTime(); 496 long start = System.nanoTime();
486 long end = start + unit.toNanos(timeout); 497 long end = start + unit.toNanos(timeout);
...@@ -494,7 +505,7 @@ public class FlowRuleManager ...@@ -494,7 +505,7 @@ public class FlowRuleManager
494 return finalizeBatchOperation(success, failed); 505 return finalizeBatchOperation(success, failed);
495 } 506 }
496 507
497 - private boolean validateBatchOperation(List<FlowEntry> failed, 508 + private boolean validateBatchOperation(Set<FlowEntry> failed,
498 CompletedBatchOperation completed) { 509 CompletedBatchOperation completed) {
499 510
500 if (isCancelled()) { 511 if (isCancelled()) {
...@@ -516,7 +527,7 @@ public class FlowRuleManager ...@@ -516,7 +527,7 @@ public class FlowRuleManager
516 } 527 }
517 528
518 private CompletedBatchOperation finalizeBatchOperation(boolean success, 529 private CompletedBatchOperation finalizeBatchOperation(boolean success,
519 - List<FlowEntry> failed) { 530 + Set<FlowEntry> failed) {
520 synchronized (this) { 531 synchronized (this) {
521 if (!state.compareAndSet(BatchState.STARTED, BatchState.FINISHED)) { 532 if (!state.compareAndSet(BatchState.STARTED, BatchState.FINISHED)) {
522 if (state.get() == BatchState.FINISHED) { 533 if (state.get() == BatchState.FINISHED) {
...@@ -539,11 +550,6 @@ public class FlowRuleManager ...@@ -539,11 +550,6 @@ public class FlowRuleManager
539 store.storeFlowRule(fbe.getTarget()); 550 store.storeFlowRule(fbe.getTarget());
540 } 551 }
541 } 552 }
542 -
543 } 553 }
544 } 554 }
545 -
546 -
547 -
548 -
549 } 555 }
......
1 package org.onlab.onos.net.flow.impl; 1 package org.onlab.onos.net.flow.impl;
2 2
3 -import static java.util.Collections.EMPTY_LIST;
4 import static org.junit.Assert.assertEquals; 3 import static org.junit.Assert.assertEquals;
5 import static org.junit.Assert.assertFalse; 4 import static org.junit.Assert.assertFalse;
6 import static org.junit.Assert.assertNotNull; 5 import static org.junit.Assert.assertNotNull;
...@@ -17,6 +16,7 @@ import java.util.List; ...@@ -17,6 +16,7 @@ import java.util.List;
17 import java.util.Map; 16 import java.util.Map;
18 import java.util.Set; 17 import java.util.Set;
19 import java.util.concurrent.ExecutionException; 18 import java.util.concurrent.ExecutionException;
19 +import java.util.concurrent.Executor;
20 import java.util.concurrent.Future; 20 import java.util.concurrent.Future;
21 import java.util.concurrent.TimeUnit; 21 import java.util.concurrent.TimeUnit;
22 import java.util.concurrent.TimeoutException; 22 import java.util.concurrent.TimeoutException;
...@@ -64,6 +64,7 @@ import com.google.common.collect.ImmutableList; ...@@ -64,6 +64,7 @@ import com.google.common.collect.ImmutableList;
64 import com.google.common.collect.ImmutableMap; 64 import com.google.common.collect.ImmutableMap;
65 import com.google.common.collect.Lists; 65 import com.google.common.collect.Lists;
66 import com.google.common.collect.Sets; 66 import com.google.common.collect.Sets;
67 +import com.google.common.util.concurrent.ListenableFuture;
67 68
68 /** 69 /**
69 * Test codifying the flow rule service & flow rule provider service contracts. 70 * Test codifying the flow rule service & flow rule provider service contracts.
...@@ -515,13 +516,13 @@ public class FlowRuleManagerTest { ...@@ -515,13 +516,13 @@ public class FlowRuleManagerTest {
515 } 516 }
516 517
517 @Override 518 @Override
518 - public Future<CompletedBatchOperation> executeBatch( 519 + public ListenableFuture<CompletedBatchOperation> executeBatch(
519 BatchOperation<FlowRuleBatchEntry> batch) { 520 BatchOperation<FlowRuleBatchEntry> batch) {
520 return new TestInstallationFuture(); 521 return new TestInstallationFuture();
521 } 522 }
522 523
523 private class TestInstallationFuture 524 private class TestInstallationFuture
524 - implements Future<CompletedBatchOperation> { 525 + implements ListenableFuture<CompletedBatchOperation> {
525 526
526 @Override 527 @Override
527 public boolean cancel(boolean mayInterruptIfRunning) { 528 public boolean cancel(boolean mayInterruptIfRunning) {
...@@ -541,7 +542,7 @@ public class FlowRuleManagerTest { ...@@ -541,7 +542,7 @@ public class FlowRuleManagerTest {
541 @Override 542 @Override
542 public CompletedBatchOperation get() 543 public CompletedBatchOperation get()
543 throws InterruptedException, ExecutionException { 544 throws InterruptedException, ExecutionException {
544 - return new CompletedBatchOperation(true, EMPTY_LIST); 545 + return new CompletedBatchOperation(true, Collections.<FlowEntry>emptySet());
545 } 546 }
546 547
547 @Override 548 @Override
...@@ -550,6 +551,11 @@ public class FlowRuleManagerTest { ...@@ -550,6 +551,11 @@ public class FlowRuleManagerTest {
550 ExecutionException, TimeoutException { 551 ExecutionException, TimeoutException {
551 return null; 552 return null;
552 } 553 }
554 +
555 + @Override
556 + public void addListener(Runnable task, Executor executor) {
557 + // TODO: add stuff.
558 + }
553 } 559 }
554 560
555 } 561 }
......
...@@ -12,4 +12,5 @@ public final class FlowStoreMessageSubjects { ...@@ -12,4 +12,5 @@ public final class FlowStoreMessageSubjects {
12 public static final MessageSubject ADD_OR_UPDATE_FLOW_RULE = 12 public static final MessageSubject ADD_OR_UPDATE_FLOW_RULE =
13 new MessageSubject("peer-forward-add-or-update-flow-rule"); 13 new MessageSubject("peer-forward-add-or-update-flow-rule");
14 public static final MessageSubject REMOVE_FLOW_RULE = new MessageSubject("peer-forward-remove-flow-rule"); 14 public static final MessageSubject REMOVE_FLOW_RULE = new MessageSubject("peer-forward-remove-flow-rule");
15 + public static final MessageSubject GET_FLOW_ENTRY = new MessageSubject("peer-forward-get-flow-entry");
15 } 16 }
......
...@@ -26,10 +26,12 @@ import org.onlab.onos.net.Port; ...@@ -26,10 +26,12 @@ import org.onlab.onos.net.Port;
26 import org.onlab.onos.net.PortNumber; 26 import org.onlab.onos.net.PortNumber;
27 import org.onlab.onos.net.device.DefaultDeviceDescription; 27 import org.onlab.onos.net.device.DefaultDeviceDescription;
28 import org.onlab.onos.net.device.DefaultPortDescription; 28 import org.onlab.onos.net.device.DefaultPortDescription;
29 +import org.onlab.onos.net.flow.DefaultFlowEntry;
29 import org.onlab.onos.net.flow.DefaultFlowRule; 30 import org.onlab.onos.net.flow.DefaultFlowRule;
30 import org.onlab.onos.net.flow.DefaultTrafficSelector; 31 import org.onlab.onos.net.flow.DefaultTrafficSelector;
31 import org.onlab.onos.net.flow.DefaultTrafficTreatment; 32 import org.onlab.onos.net.flow.DefaultTrafficTreatment;
32 import org.onlab.onos.net.flow.FlowId; 33 import org.onlab.onos.net.flow.FlowId;
34 +import org.onlab.onos.net.flow.StoredFlowEntry;
33 import org.onlab.onos.net.flow.criteria.Criteria; 35 import org.onlab.onos.net.flow.criteria.Criteria;
34 import org.onlab.onos.net.flow.criteria.Criterion; 36 import org.onlab.onos.net.flow.criteria.Criterion;
35 import org.onlab.onos.net.flow.instructions.Instructions; 37 import org.onlab.onos.net.flow.instructions.Instructions;
...@@ -93,6 +95,8 @@ public final class KryoNamespaces { ...@@ -93,6 +95,8 @@ public final class KryoNamespaces {
93 HostId.class, 95 HostId.class,
94 HostDescription.class, 96 HostDescription.class,
95 DefaultHostDescription.class, 97 DefaultHostDescription.class,
98 + DefaultFlowEntry.class,
99 + StoredFlowEntry.class,
96 DefaultFlowRule.class, 100 DefaultFlowRule.class,
97 FlowId.class, 101 FlowId.class,
98 DefaultTrafficSelector.class, 102 DefaultTrafficSelector.class,
......
...@@ -3,6 +3,8 @@ package org.onlab.onos.store.trivial.impl; ...@@ -3,6 +3,8 @@ package org.onlab.onos.store.trivial.impl;
3 import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_REMOVED; 3 import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_REMOVED;
4 import static org.slf4j.LoggerFactory.getLogger; 4 import static org.slf4j.LoggerFactory.getLogger;
5 import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked; 5 import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
6 +
7 +import java.util.Arrays;
6 import java.util.Collections; 8 import java.util.Collections;
7 import java.util.HashSet; 9 import java.util.HashSet;
8 import java.util.List; 10 import java.util.List;
...@@ -10,6 +12,7 @@ import java.util.Set; ...@@ -10,6 +12,7 @@ import java.util.Set;
10 import java.util.concurrent.ConcurrentHashMap; 12 import java.util.concurrent.ConcurrentHashMap;
11 import java.util.concurrent.ConcurrentMap; 13 import java.util.concurrent.ConcurrentMap;
12 import java.util.concurrent.CopyOnWriteArrayList; 14 import java.util.concurrent.CopyOnWriteArrayList;
15 +import java.util.concurrent.Future;
13 16
14 import org.apache.felix.scr.annotations.Activate; 17 import org.apache.felix.scr.annotations.Activate;
15 import org.apache.felix.scr.annotations.Component; 18 import org.apache.felix.scr.annotations.Component;
...@@ -17,11 +20,17 @@ import org.apache.felix.scr.annotations.Deactivate; ...@@ -17,11 +20,17 @@ import org.apache.felix.scr.annotations.Deactivate;
17 import org.apache.felix.scr.annotations.Service; 20 import org.apache.felix.scr.annotations.Service;
18 import org.onlab.onos.ApplicationId; 21 import org.onlab.onos.ApplicationId;
19 import org.onlab.onos.net.DeviceId; 22 import org.onlab.onos.net.DeviceId;
23 +import org.onlab.onos.net.flow.CompletedBatchOperation;
20 import org.onlab.onos.net.flow.DefaultFlowEntry; 24 import org.onlab.onos.net.flow.DefaultFlowEntry;
21 import org.onlab.onos.net.flow.FlowEntry; 25 import org.onlab.onos.net.flow.FlowEntry;
22 import org.onlab.onos.net.flow.FlowEntry.FlowEntryState; 26 import org.onlab.onos.net.flow.FlowEntry.FlowEntryState;
23 import org.onlab.onos.net.flow.FlowId; 27 import org.onlab.onos.net.flow.FlowId;
24 import org.onlab.onos.net.flow.FlowRule; 28 import org.onlab.onos.net.flow.FlowRule;
29 +import org.onlab.onos.net.flow.FlowRuleBatchEntry;
30 +import org.onlab.onos.net.flow.FlowRuleBatchEntry.FlowRuleOperation;
31 +import org.onlab.onos.net.flow.FlowRuleBatchEvent;
32 +import org.onlab.onos.net.flow.FlowRuleBatchOperation;
33 +import org.onlab.onos.net.flow.FlowRuleBatchRequest;
25 import org.onlab.onos.net.flow.FlowRuleEvent; 34 import org.onlab.onos.net.flow.FlowRuleEvent;
26 import org.onlab.onos.net.flow.FlowRuleEvent.Type; 35 import org.onlab.onos.net.flow.FlowRuleEvent.Type;
27 import org.onlab.onos.net.flow.FlowRuleStore; 36 import org.onlab.onos.net.flow.FlowRuleStore;
...@@ -33,6 +42,7 @@ import org.slf4j.Logger; ...@@ -33,6 +42,7 @@ import org.slf4j.Logger;
33 42
34 import com.google.common.base.Function; 43 import com.google.common.base.Function;
35 import com.google.common.collect.FluentIterable; 44 import com.google.common.collect.FluentIterable;
45 +import com.google.common.util.concurrent.Futures;
36 46
37 /** 47 /**
38 * Manages inventory of flow rules using trivial in-memory implementation. 48 * Manages inventory of flow rules using trivial in-memory implementation.
...@@ -40,7 +50,7 @@ import com.google.common.collect.FluentIterable; ...@@ -40,7 +50,7 @@ import com.google.common.collect.FluentIterable;
40 @Component(immediate = true) 50 @Component(immediate = true)
41 @Service 51 @Service
42 public class SimpleFlowRuleStore 52 public class SimpleFlowRuleStore
43 - extends AbstractStore<FlowRuleEvent, FlowRuleStoreDelegate> 53 + extends AbstractStore<FlowRuleBatchEvent, FlowRuleStoreDelegate>
44 implements FlowRuleStore { 54 implements FlowRuleStore {
45 55
46 private final Logger log = getLogger(getClass()); 56 private final Logger log = getLogger(getClass());
...@@ -148,12 +158,11 @@ public class SimpleFlowRuleStore ...@@ -148,12 +158,11 @@ public class SimpleFlowRuleStore
148 } 158 }
149 159
150 @Override 160 @Override
151 - public boolean storeFlowRule(FlowRule rule) { 161 + public void storeFlowRule(FlowRule rule) {
152 - final boolean added = storeFlowRuleInternal(rule); 162 + storeFlowRuleInternal(rule);
153 - return added;
154 } 163 }
155 164
156 - private boolean storeFlowRuleInternal(FlowRule rule) { 165 + private void storeFlowRuleInternal(FlowRule rule) {
157 StoredFlowEntry f = new DefaultFlowEntry(rule); 166 StoredFlowEntry f = new DefaultFlowEntry(rule);
158 final DeviceId did = f.deviceId(); 167 final DeviceId did = f.deviceId();
159 final FlowId fid = f.id(); 168 final FlowId fid = f.id();
...@@ -162,19 +171,20 @@ public class SimpleFlowRuleStore ...@@ -162,19 +171,20 @@ public class SimpleFlowRuleStore
162 for (StoredFlowEntry fe : existing) { 171 for (StoredFlowEntry fe : existing) {
163 if (fe.equals(rule)) { 172 if (fe.equals(rule)) {
164 // was already there? ignore 173 // was already there? ignore
165 - return false; 174 + return;
166 } 175 }
167 } 176 }
168 // new flow rule added 177 // new flow rule added
169 existing.add(f); 178 existing.add(f);
170 - // TODO: Should we notify only if it's "remote" event? 179 + notifyDelegate(FlowRuleBatchEvent.create(
171 - //notifyDelegate(new FlowRuleEvent(Type.RULE_ADD_REQUESTED, rule)); 180 + new FlowRuleBatchRequest(
172 - return true; 181 + Arrays.<FlowEntry>asList(f),
182 + Collections.<FlowEntry>emptyList())));
173 } 183 }
174 } 184 }
175 185
176 @Override 186 @Override
177 - public boolean deleteFlowRule(FlowRule rule) { 187 + public void deleteFlowRule(FlowRule rule) {
178 188
179 List<StoredFlowEntry> entries = getFlowEntries(rule.deviceId(), rule.id()); 189 List<StoredFlowEntry> entries = getFlowEntries(rule.deviceId(), rule.id());
180 synchronized (entries) { 190 synchronized (entries) {
...@@ -184,13 +194,11 @@ public class SimpleFlowRuleStore ...@@ -184,13 +194,11 @@ public class SimpleFlowRuleStore
184 entry.setState(FlowEntryState.PENDING_REMOVE); 194 entry.setState(FlowEntryState.PENDING_REMOVE);
185 // TODO: Should we notify only if it's "remote" event? 195 // TODO: Should we notify only if it's "remote" event?
186 //notifyDelegate(new FlowRuleEvent(Type.RULE_REMOVE_REQUESTED, rule)); 196 //notifyDelegate(new FlowRuleEvent(Type.RULE_REMOVE_REQUESTED, rule));
187 - return true;
188 } 197 }
189 } 198 }
190 } 199 }
191 } 200 }
192 //log.warn("Cannot find rule {}", rule); 201 //log.warn("Cannot find rule {}", rule);
193 - return false;
194 } 202 }
195 203
196 @Override 204 @Override
...@@ -236,4 +244,24 @@ public class SimpleFlowRuleStore ...@@ -236,4 +244,24 @@ public class SimpleFlowRuleStore
236 } 244 }
237 return null; 245 return null;
238 } 246 }
247 +
248 + @Override
249 + public Future<CompletedBatchOperation> storeBatch(
250 + FlowRuleBatchOperation batchOperation) {
251 + for (FlowRuleBatchEntry entry : batchOperation.getOperations()) {
252 + if (entry.getOperator().equals(FlowRuleOperation.ADD)) {
253 + storeFlowRule(entry.getTarget());
254 + } else if (entry.getOperator().equals(FlowRuleOperation.REMOVE)) {
255 + deleteFlowRule(entry.getTarget());
256 + } else {
257 + throw new UnsupportedOperationException("Unsupported operation type");
258 + }
259 + }
260 + return Futures.immediateFuture(new CompletedBatchOperation(true, Collections.<FlowEntry>emptySet()));
261 + }
262 +
263 + @Override
264 + public void batchOperationComplete(FlowRuleBatchEvent event) {
265 + notifyDelegate(event);
266 + }
239 } 267 }
......
...@@ -10,7 +10,7 @@ import java.util.Set; ...@@ -10,7 +10,7 @@ import java.util.Set;
10 import java.util.concurrent.ConcurrentHashMap; 10 import java.util.concurrent.ConcurrentHashMap;
11 import java.util.concurrent.CountDownLatch; 11 import java.util.concurrent.CountDownLatch;
12 import java.util.concurrent.ExecutionException; 12 import java.util.concurrent.ExecutionException;
13 -import java.util.concurrent.Future; 13 +import java.util.concurrent.Executor;
14 import java.util.concurrent.TimeUnit; 14 import java.util.concurrent.TimeUnit;
15 import java.util.concurrent.TimeoutException; 15 import java.util.concurrent.TimeoutException;
16 import java.util.concurrent.atomic.AtomicBoolean; 16 import java.util.concurrent.atomic.AtomicBoolean;
...@@ -69,9 +69,11 @@ import org.projectfloodlight.openflow.types.U32; ...@@ -69,9 +69,11 @@ import org.projectfloodlight.openflow.types.U32;
69 import org.slf4j.Logger; 69 import org.slf4j.Logger;
70 70
71 import com.google.common.collect.ArrayListMultimap; 71 import com.google.common.collect.ArrayListMultimap;
72 -import com.google.common.collect.Lists;
73 import com.google.common.collect.Maps; 72 import com.google.common.collect.Maps;
74 import com.google.common.collect.Multimap; 73 import com.google.common.collect.Multimap;
74 +import com.google.common.collect.Sets;
75 +import com.google.common.util.concurrent.ExecutionList;
76 +import com.google.common.util.concurrent.ListenableFuture;
75 77
76 /** 78 /**
77 * Provider which uses an OpenFlow controller to detect network 79 * Provider which uses an OpenFlow controller to detect network
...@@ -97,6 +99,8 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -97,6 +99,8 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
97 99
98 private final InternalFlowProvider listener = new InternalFlowProvider(); 100 private final InternalFlowProvider listener = new InternalFlowProvider();
99 101
102 + // FIXME: This should be an expiring map to ensure futures that don't have
103 + // a future eventually get garbage collected.
100 private final Map<Long, InstallationFuture> pendingFutures = 104 private final Map<Long, InstallationFuture> pendingFutures =
101 new ConcurrentHashMap<Long, InstallationFuture>(); 105 new ConcurrentHashMap<Long, InstallationFuture>();
102 106
...@@ -159,7 +163,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -159,7 +163,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
159 } 163 }
160 164
161 @Override 165 @Override
162 - public Future<CompletedBatchOperation> executeBatch(BatchOperation<FlowRuleBatchEntry> batch) { 166 + public ListenableFuture<CompletedBatchOperation> executeBatch(BatchOperation<FlowRuleBatchEntry> batch) {
163 final Set<Dpid> sws = 167 final Set<Dpid> sws =
164 Collections.newSetFromMap(new ConcurrentHashMap<Dpid, Boolean>()); 168 Collections.newSetFromMap(new ConcurrentHashMap<Dpid, Boolean>());
165 final Map<Long, FlowRuleBatchEntry> fmXids = new HashMap<Long, FlowRuleBatchEntry>(); 169 final Map<Long, FlowRuleBatchEntry> fmXids = new HashMap<Long, FlowRuleBatchEntry>();
...@@ -315,18 +319,20 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -315,18 +319,20 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
315 } 319 }
316 } 320 }
317 321
318 - private class InstallationFuture implements Future<CompletedBatchOperation> { 322 + private class InstallationFuture implements ListenableFuture<CompletedBatchOperation> {
319 323
320 private final Set<Dpid> sws; 324 private final Set<Dpid> sws;
321 private final AtomicBoolean ok = new AtomicBoolean(true); 325 private final AtomicBoolean ok = new AtomicBoolean(true);
322 private final Map<Long, FlowRuleBatchEntry> fms; 326 private final Map<Long, FlowRuleBatchEntry> fms;
323 327
324 - private final List<FlowEntry> offendingFlowMods = Lists.newLinkedList(); 328 + private final Set<FlowEntry> offendingFlowMods = Sets.newHashSet();
325 329
326 private final CountDownLatch countDownLatch; 330 private final CountDownLatch countDownLatch;
327 private Long pendingXid; 331 private Long pendingXid;
328 private BatchState state; 332 private BatchState state;
329 333
334 + private final ExecutionList executionList = new ExecutionList();
335 +
330 public InstallationFuture(Set<Dpid> sws, Map<Long, FlowRuleBatchEntry> fmXids) { 336 public InstallationFuture(Set<Dpid> sws, Map<Long, FlowRuleBatchEntry> fmXids) {
331 this.state = BatchState.STARTED; 337 this.state = BatchState.STARTED;
332 this.sws = sws; 338 this.sws = sws;
...@@ -335,6 +341,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -335,6 +341,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
335 } 341 }
336 342
337 public void fail(OFErrorMsg msg, Dpid dpid) { 343 public void fail(OFErrorMsg msg, Dpid dpid) {
344 +
338 ok.set(false); 345 ok.set(false);
339 removeRequirement(dpid); 346 removeRequirement(dpid);
340 FlowEntry fe = null; 347 FlowEntry fe = null;
...@@ -407,6 +414,9 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -407,6 +414,9 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
407 414
408 @Override 415 @Override
409 public boolean cancel(boolean mayInterruptIfRunning) { 416 public boolean cancel(boolean mayInterruptIfRunning) {
417 + if (isDone()) {
418 + return false;
419 + }
410 ok.set(false); 420 ok.set(false);
411 this.state = BatchState.CANCELLED; 421 this.state = BatchState.CANCELLED;
412 cleanUp(); 422 cleanUp();
...@@ -419,7 +429,8 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -419,7 +429,8 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
419 } 429 }
420 430
421 } 431 }
422 - return isCancelled(); 432 + invokeCallbacks();
433 + return true;
423 } 434 }
424 435
425 @Override 436 @Override
...@@ -429,14 +440,15 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -429,14 +440,15 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
429 440
430 @Override 441 @Override
431 public boolean isDone() { 442 public boolean isDone() {
432 - return this.state == BatchState.FINISHED; 443 + return this.state == BatchState.FINISHED || isCancelled();
433 } 444 }
434 445
435 @Override 446 @Override
436 public CompletedBatchOperation get() throws InterruptedException, ExecutionException { 447 public CompletedBatchOperation get() throws InterruptedException, ExecutionException {
437 countDownLatch.await(); 448 countDownLatch.await();
438 this.state = BatchState.FINISHED; 449 this.state = BatchState.FINISHED;
439 - return new CompletedBatchOperation(ok.get(), offendingFlowMods); 450 + CompletedBatchOperation result = new CompletedBatchOperation(ok.get(), offendingFlowMods);
451 + return result;
440 } 452 }
441 453
442 @Override 454 @Override
...@@ -445,7 +457,8 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -445,7 +457,8 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
445 TimeoutException { 457 TimeoutException {
446 if (countDownLatch.await(timeout, unit)) { 458 if (countDownLatch.await(timeout, unit)) {
447 this.state = BatchState.FINISHED; 459 this.state = BatchState.FINISHED;
448 - return new CompletedBatchOperation(ok.get(), offendingFlowMods); 460 + CompletedBatchOperation result = new CompletedBatchOperation(ok.get(), offendingFlowMods);
461 + return result;
449 } 462 }
450 throw new TimeoutException(); 463 throw new TimeoutException();
451 } 464 }
...@@ -463,10 +476,21 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -463,10 +476,21 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
463 476
464 private void removeRequirement(Dpid dpid) { 477 private void removeRequirement(Dpid dpid) {
465 countDownLatch.countDown(); 478 countDownLatch.countDown();
479 + if (countDownLatch.getCount() == 0) {
480 + invokeCallbacks();
481 + }
466 sws.remove(dpid); 482 sws.remove(dpid);
467 cleanUp(); 483 cleanUp();
468 } 484 }
469 485
486 + @Override
487 + public void addListener(Runnable runnable, Executor executor) {
488 + executionList.add(runnable, executor);
489 + }
490 +
491 + private void invokeCallbacks() {
492 + executionList.execute();
493 + }
470 } 494 }
471 495
472 } 496 }
......