Brian O'Connor
Committed by Gerrit Code Review

Updating Intent Manager to deal with failures.

Added ids to Flow batch futures.
Adding some basic unit tests for IntentManger
Adding failedIds to the completedOperation in FlowRuleManager

Change-Id: I7645cead193299f70d319d254cd1e82d96909e7b
Showing 20 changed files with 882 additions and 320 deletions
...@@ -15,6 +15,9 @@ ...@@ -15,6 +15,9 @@
15 */ 15 */
16 package org.onlab.onos.net.flow; 16 package org.onlab.onos.net.flow;
17 17
18 +
19 +import java.util.Collections;
20 +
18 import java.util.Set; 21 import java.util.Set;
19 22
20 import com.google.common.collect.ImmutableSet; 23 import com.google.common.collect.ImmutableSet;
...@@ -26,6 +29,21 @@ public class CompletedBatchOperation implements BatchOperationResult<FlowRule> { ...@@ -26,6 +29,21 @@ public class CompletedBatchOperation implements BatchOperationResult<FlowRule> {
26 29
27 private final boolean success; 30 private final boolean success;
28 private final Set<FlowRule> failures; 31 private final Set<FlowRule> failures;
32 + private final Set<Long> failedIds;
33 +
34 + /**
35 + * Creates a new batch completion result.
36 + *
37 + * @param success indicates whether the completion is successful.
38 + * @param failures set of any failures encountered
39 + * @param failedIds (optional) set of failed operation ids
40 + */
41 + public CompletedBatchOperation(boolean success, Set<? extends FlowRule> failures,
42 + Set<Long> failedIds) {
43 + this.success = success;
44 + this.failures = ImmutableSet.copyOf(failures);
45 + this.failedIds = ImmutableSet.copyOf(failedIds);
46 + }
29 47
30 /** 48 /**
31 * Creates a new batch completion result. 49 * Creates a new batch completion result.
...@@ -36,8 +54,11 @@ public class CompletedBatchOperation implements BatchOperationResult<FlowRule> { ...@@ -36,8 +54,11 @@ public class CompletedBatchOperation implements BatchOperationResult<FlowRule> {
36 public CompletedBatchOperation(boolean success, Set<? extends FlowRule> failures) { 54 public CompletedBatchOperation(boolean success, Set<? extends FlowRule> failures) {
37 this.success = success; 55 this.success = success;
38 this.failures = ImmutableSet.copyOf(failures); 56 this.failures = ImmutableSet.copyOf(failures);
57 + this.failedIds = Collections.emptySet();
39 } 58 }
40 59
60 +
61 +
41 @Override 62 @Override
42 public boolean isSuccess() { 63 public boolean isSuccess() {
43 return success; 64 return success;
...@@ -48,4 +69,8 @@ public class CompletedBatchOperation implements BatchOperationResult<FlowRule> { ...@@ -48,4 +69,8 @@ public class CompletedBatchOperation implements BatchOperationResult<FlowRule> {
48 return failures; 69 return failures;
49 } 70 }
50 71
72 + public Set<Long> failedIds() {
73 + return failedIds;
74 + }
75 +
51 } 76 }
......
...@@ -21,8 +21,20 @@ import org.onlab.onos.net.flow.FlowRuleBatchEntry.FlowRuleOperation; ...@@ -21,8 +21,20 @@ import org.onlab.onos.net.flow.FlowRuleBatchEntry.FlowRuleOperation;
21 public class FlowRuleBatchEntry 21 public class FlowRuleBatchEntry
22 extends BatchOperationEntry<FlowRuleOperation, FlowRule> { 22 extends BatchOperationEntry<FlowRuleOperation, FlowRule> {
23 23
24 + private final Long id; // FIXME: consider using Optional<Long>
25 +
24 public FlowRuleBatchEntry(FlowRuleOperation operator, FlowRule target) { 26 public FlowRuleBatchEntry(FlowRuleOperation operator, FlowRule target) {
25 super(operator, target); 27 super(operator, target);
28 + this.id = null;
29 + }
30 +
31 + public FlowRuleBatchEntry(FlowRuleOperation operator, FlowRule target, Long id) {
32 + super(operator, target);
33 + this.id = id;
34 + }
35 +
36 + public Long id() {
37 + return id;
26 } 38 }
27 39
28 public enum FlowRuleOperation { 40 public enum FlowRuleOperation {
......
...@@ -18,38 +18,52 @@ package org.onlab.onos.net.flow; ...@@ -18,38 +18,52 @@ package org.onlab.onos.net.flow;
18 import java.util.Collections; 18 import java.util.Collections;
19 import java.util.List; 19 import java.util.List;
20 20
21 -import org.onlab.onos.net.flow.FlowRuleBatchEntry.FlowRuleOperation; 21 +import com.google.common.base.Function;
22 +import com.google.common.collect.FluentIterable;
23 +
24 +
22 25
23 import com.google.common.collect.Lists; 26 import com.google.common.collect.Lists;
24 27
25 public class FlowRuleBatchRequest { 28 public class FlowRuleBatchRequest {
26 29
27 private final int batchId; 30 private final int batchId;
28 - private final List<FlowRule> toAdd; 31 + private final List<FlowRuleBatchEntry> toAdd;
29 - private final List<FlowRule> toRemove; 32 + private final List<FlowRuleBatchEntry> toRemove;
30 33
31 - public FlowRuleBatchRequest(int batchId, List<? extends FlowRule> toAdd, List<? extends FlowRule> toRemove) { 34 + public FlowRuleBatchRequest(int batchId, List<FlowRuleBatchEntry> toAdd,
35 + List<FlowRuleBatchEntry> toRemove) {
32 this.batchId = batchId; 36 this.batchId = batchId;
33 this.toAdd = Collections.unmodifiableList(toAdd); 37 this.toAdd = Collections.unmodifiableList(toAdd);
34 this.toRemove = Collections.unmodifiableList(toRemove); 38 this.toRemove = Collections.unmodifiableList(toRemove);
35 } 39 }
36 40
37 public List<FlowRule> toAdd() { 41 public List<FlowRule> toAdd() {
38 - return toAdd; 42 + return FluentIterable.from(toAdd).transform(
43 + new Function<FlowRuleBatchEntry, FlowRule>() {
44 +
45 + @Override
46 + public FlowRule apply(FlowRuleBatchEntry input) {
47 + return input.getTarget();
48 + }
49 + }).toList();
39 } 50 }
40 51
41 public List<FlowRule> toRemove() { 52 public List<FlowRule> toRemove() {
42 - return toRemove; 53 + return FluentIterable.from(toRemove).transform(
54 + new Function<FlowRuleBatchEntry, FlowRule>() {
55 +
56 + @Override
57 + public FlowRule apply(FlowRuleBatchEntry input) {
58 + return input.getTarget();
59 + }
60 + }).toList();
43 } 61 }
44 62
45 public FlowRuleBatchOperation asBatchOperation() { 63 public FlowRuleBatchOperation asBatchOperation() {
46 List<FlowRuleBatchEntry> entries = Lists.newArrayList(); 64 List<FlowRuleBatchEntry> entries = Lists.newArrayList();
47 - for (FlowRule e : toAdd) { 65 + entries.addAll(toAdd);
48 - entries.add(new FlowRuleBatchEntry(FlowRuleOperation.ADD, e)); 66 + entries.addAll(toRemove);
49 - }
50 - for (FlowRule e : toRemove) {
51 - entries.add(new FlowRuleBatchEntry(FlowRuleOperation.REMOVE, e));
52 - }
53 return new FlowRuleBatchOperation(entries); 67 return new FlowRuleBatchOperation(entries);
54 } 68 }
55 69
......
...@@ -37,10 +37,16 @@ public interface IntentBatchService { ...@@ -37,10 +37,16 @@ public interface IntentBatchService {
37 void removeIntentOperations(IntentOperations operations); 37 void removeIntentOperations(IntentOperations operations);
38 38
39 /** 39 /**
40 - * Returns the set of intent batches currently being tracked. 40 + * Returns the set of intent batches that are pending.
41 * @return set of batches 41 * @return set of batches
42 */ 42 */
43 - Set<IntentOperations> getIntentOperations(); 43 + Set<IntentOperations> getPendingOperations();
44 +
45 + /**
46 + * Returns the set of intent batches currently being processed.
47 + * @return set of batches
48 + */
49 + Set<IntentOperations> getCurrentOperations();
44 50
45 /** 51 /**
46 * Sets the batch service delegate. 52 * Sets the batch service delegate.
......
...@@ -67,6 +67,18 @@ public enum IntentState { ...@@ -67,6 +67,18 @@ public enum IntentState {
67 RECOMPILING, 67 RECOMPILING,
68 68
69 /** 69 /**
70 + * TODO: Indicated that an intent will soon be recompiled.
71 + */
72 + //UPDATE,
73 +
74 + /**
75 + * TODO.
76 + * Indicates that an application has requested that an intent be withdrawn.
77 + * It will start withdrawing short, but not necessarily on this instance.
78 + */
79 + //WITHDRAW_REQ,
80 +
81 + /**
70 * Indicates that the intent is being withdrawn. This is a transitional 82 * Indicates that the intent is being withdrawn. This is a transitional
71 * state, triggered by invocation of the 83 * state, triggered by invocation of the
72 * {@link IntentService#withdraw(Intent)} but one with only one outcome, 84 * {@link IntentService#withdraw(Intent)} but one with only one outcome,
......
...@@ -21,6 +21,8 @@ import java.util.List; ...@@ -21,6 +21,8 @@ import java.util.List;
21 import org.junit.Test; 21 import org.junit.Test;
22 import org.onlab.onos.net.intent.IntentTestsMocks; 22 import org.onlab.onos.net.intent.IntentTestsMocks;
23 23
24 +import static org.onlab.onos.net.flow.FlowRuleBatchEntry.FlowRuleOperation.*;
25 +
24 import static org.hamcrest.MatcherAssert.assertThat; 26 import static org.hamcrest.MatcherAssert.assertThat;
25 import static org.hamcrest.Matchers.hasSize; 27 import static org.hamcrest.Matchers.hasSize;
26 import static org.hamcrest.Matchers.is; 28 import static org.hamcrest.Matchers.is;
...@@ -38,10 +40,10 @@ public class FlowRuleBatchRequestTest { ...@@ -38,10 +40,10 @@ public class FlowRuleBatchRequestTest {
38 public void testConstruction() { 40 public void testConstruction() {
39 final FlowRule rule1 = new IntentTestsMocks.MockFlowRule(1); 41 final FlowRule rule1 = new IntentTestsMocks.MockFlowRule(1);
40 final FlowRule rule2 = new IntentTestsMocks.MockFlowRule(2); 42 final FlowRule rule2 = new IntentTestsMocks.MockFlowRule(2);
41 - final List<FlowRule> toAdd = new LinkedList<>(); 43 + final List<FlowRuleBatchEntry> toAdd = new LinkedList<>();
42 - toAdd.add(rule1); 44 + toAdd.add(new FlowRuleBatchEntry(ADD, rule1));
43 - final List<FlowRule> toRemove = new LinkedList<>(); 45 + final List<FlowRuleBatchEntry> toRemove = new LinkedList<>();
44 - toRemove.add(rule2); 46 + toRemove.add(new FlowRuleBatchEntry(REMOVE, rule2));
45 47
46 48
47 final FlowRuleBatchRequest request = 49 final FlowRuleBatchRequest request =
......
...@@ -26,6 +26,7 @@ import java.util.Collections; ...@@ -26,6 +26,7 @@ import java.util.Collections;
26 import java.util.HashSet; 26 import java.util.HashSet;
27 import java.util.LinkedList; 27 import java.util.LinkedList;
28 import java.util.List; 28 import java.util.List;
29 +import java.util.Objects;
29 import java.util.Set; 30 import java.util.Set;
30 31
31 import org.onlab.onos.net.DeviceId; 32 import org.onlab.onos.net.DeviceId;
...@@ -331,7 +332,22 @@ public class IntentTestsMocks { ...@@ -331,7 +332,22 @@ public class IntentTestsMocks {
331 return false; 332 return false;
332 } 333 }
333 334
335 + @Override
336 + public int hashCode() {
337 + return Objects.hash(priority);
338 + }
334 339
340 + @Override
341 + public boolean equals(Object obj) {
342 + if (this == obj) {
343 + return true;
344 + }
345 + if (obj == null || getClass() != obj.getClass()) {
346 + return false;
347 + }
348 + final MockFlowRule other = (MockFlowRule) obj;
349 + return Objects.equals(this.priority, other.priority);
350 + }
335 } 351 }
336 352
337 353
......
...@@ -488,13 +488,14 @@ public class FlowRuleManager ...@@ -488,13 +488,14 @@ public class FlowRuleManager
488 488
489 boolean success = true; 489 boolean success = true;
490 Set<FlowRule> failed = Sets.newHashSet(); 490 Set<FlowRule> failed = Sets.newHashSet();
491 + Set<Long> failedIds = Sets.newHashSet();
491 CompletedBatchOperation completed; 492 CompletedBatchOperation completed;
492 for (Future<CompletedBatchOperation> future : futures) { 493 for (Future<CompletedBatchOperation> future : futures) {
493 completed = future.get(); 494 completed = future.get();
494 - success = validateBatchOperation(failed, completed); 495 + success = validateBatchOperation(failed, failedIds, completed);
495 } 496 }
496 497
497 - return finalizeBatchOperation(success, failed); 498 + return finalizeBatchOperation(success, failed, failedIds);
498 499
499 } 500 }
500 501
...@@ -508,22 +509,25 @@ public class FlowRuleManager ...@@ -508,22 +509,25 @@ public class FlowRuleManager
508 } 509 }
509 boolean success = true; 510 boolean success = true;
510 Set<FlowRule> failed = Sets.newHashSet(); 511 Set<FlowRule> failed = Sets.newHashSet();
512 + Set<Long> failedIds = Sets.newHashSet();
511 CompletedBatchOperation completed; 513 CompletedBatchOperation completed;
512 for (Future<CompletedBatchOperation> future : futures) { 514 for (Future<CompletedBatchOperation> future : futures) {
513 completed = future.get(timeout, unit); 515 completed = future.get(timeout, unit);
514 - success = validateBatchOperation(failed, completed); 516 + success = validateBatchOperation(failed, failedIds, completed);
515 } 517 }
516 - return finalizeBatchOperation(success, failed); 518 + return finalizeBatchOperation(success, failed, failedIds);
517 } 519 }
518 520
519 private boolean validateBatchOperation(Set<FlowRule> failed, 521 private boolean validateBatchOperation(Set<FlowRule> failed,
520 - CompletedBatchOperation completed) { 522 + Set<Long> failedIds,
523 + CompletedBatchOperation completed) {
521 524
522 if (isCancelled()) { 525 if (isCancelled()) {
523 throw new CancellationException(); 526 throw new CancellationException();
524 } 527 }
525 if (!completed.isSuccess()) { 528 if (!completed.isSuccess()) {
526 failed.addAll(completed.failedItems()); 529 failed.addAll(completed.failedItems());
530 + failedIds.addAll(completed.failedIds());
527 cleanUpBatch(); 531 cleanUpBatch();
528 cancelAllSubBatches(); 532 cancelAllSubBatches();
529 return false; 533 return false;
...@@ -538,7 +542,8 @@ public class FlowRuleManager ...@@ -538,7 +542,8 @@ public class FlowRuleManager
538 } 542 }
539 543
540 private CompletedBatchOperation finalizeBatchOperation(boolean success, 544 private CompletedBatchOperation finalizeBatchOperation(boolean success,
541 - Set<FlowRule> failed) { 545 + Set<FlowRule> failed,
546 + Set<Long> failedIds) {
542 synchronized (this) { 547 synchronized (this) {
543 if (!state.compareAndSet(BatchState.STARTED, BatchState.FINISHED)) { 548 if (!state.compareAndSet(BatchState.STARTED, BatchState.FINISHED)) {
544 if (state.get() == BatchState.FINISHED) { 549 if (state.get() == BatchState.FINISHED) {
...@@ -546,7 +551,7 @@ public class FlowRuleManager ...@@ -546,7 +551,7 @@ public class FlowRuleManager
546 } 551 }
547 throw new CancellationException(); 552 throw new CancellationException();
548 } 553 }
549 - overall = new CompletedBatchOperation(success, failed); 554 + overall = new CompletedBatchOperation(success, failed, failedIds);
550 return overall; 555 return overall;
551 } 556 }
552 } 557 }
......
...@@ -49,9 +49,9 @@ import org.onlab.onos.net.intent.IntentStoreDelegate; ...@@ -49,9 +49,9 @@ import org.onlab.onos.net.intent.IntentStoreDelegate;
49 import org.slf4j.Logger; 49 import org.slf4j.Logger;
50 50
51 import java.util.ArrayList; 51 import java.util.ArrayList;
52 +import java.util.Collections;
52 import java.util.List; 53 import java.util.List;
53 import java.util.Map; 54 import java.util.Map;
54 -import java.util.Objects;
55 import java.util.concurrent.ConcurrentHashMap; 55 import java.util.concurrent.ConcurrentHashMap;
56 import java.util.concurrent.ConcurrentMap; 56 import java.util.concurrent.ConcurrentMap;
57 import java.util.concurrent.ExecutionException; 57 import java.util.concurrent.ExecutionException;
...@@ -330,12 +330,12 @@ public class IntentManager ...@@ -330,12 +330,12 @@ public class IntentManager
330 batches.addAll(getInstaller(installable).install(installable)); 330 batches.addAll(getInstaller(installable).install(installable));
331 } catch (IntentException e) { 331 } catch (IntentException e) {
332 log.warn("Unable to install intent {} due to:", update.newIntent().id(), e); 332 log.warn("Unable to install intent {} due to:", update.newIntent().id(), e);
333 + trackerService.removeTrackedResources(update.newIntent().id(),
334 + installable.resources());
333 //FIXME we failed... intent should be recompiled 335 //FIXME we failed... intent should be recompiled
334 - // TODO: remove resources
335 - // recompile!!!
336 } 336 }
337 } 337 }
338 - update.setBatches(batches); 338 + update.addBatches(batches);
339 } 339 }
340 340
341 /** 341 /**
...@@ -348,80 +348,32 @@ public class IntentManager ...@@ -348,80 +348,32 @@ public class IntentManager
348 if (!update.oldIntent().equals(update.newIntent())) { 348 if (!update.oldIntent().equals(update.newIntent())) {
349 update.setState(update.oldIntent(), WITHDRAWING); 349 update.setState(update.oldIntent(), WITHDRAWING);
350 } // else newIntent is FAILED 350 } // else newIntent is FAILED
351 - uninstallIntent(update); 351 + update.addBatches(uninstallIntent(update.oldIntent(), update.oldInstallables()));
352 -
353 - // If all went well, disassociate the top-level intent with its
354 - // installable derivatives and mark it as withdrawn.
355 - // FIXME need to clean up
356 - //store.removeInstalledIntents(intent.id());
357 } 352 }
358 353
359 /** 354 /**
360 * Uninstalls all installable intents associated with the given intent. 355 * Uninstalls all installable intents associated with the given intent.
361 * 356 *
362 - * @param update intent update 357 + * @param intent intent
358 + * @param installables installable intents
359 + * @return list of batches to uninstall intent
363 */ 360 */
364 - //FIXME: need to handle next state properly 361 + private List<FlowRuleBatchOperation> uninstallIntent(Intent intent, List<Intent> installables) {
365 - private void uninstallIntent(IntentUpdate update) { 362 + if (installables == null) {
366 - if (update.oldInstallables == null) { 363 + return Collections.emptyList();
367 - return;
368 } 364 }
369 List<FlowRuleBatchOperation> batches = Lists.newArrayList(); 365 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
370 - for (Intent installable : update.oldInstallables()) { 366 + for (Intent installable : installables) {
371 - trackerService.removeTrackedResources(update.oldIntent().id(), 367 + trackerService.removeTrackedResources(intent.id(),
372 installable.resources()); 368 installable.resources());
373 try { 369 try {
374 batches.addAll(getInstaller(installable).uninstall(installable)); 370 batches.addAll(getInstaller(installable).uninstall(installable));
375 } catch (IntentException e) { 371 } catch (IntentException e) {
376 - log.warn("Unable to uninstall intent {} due to:", update.oldIntent().id(), e); 372 + log.warn("Unable to uninstall intent {} due to:", intent.id(), e);
377 // TODO: this should never happen. but what if it does? 373 // TODO: this should never happen. but what if it does?
378 } 374 }
379 } 375 }
380 - update.setBatches(batches); 376 + return batches;
381 - // FIXME: next state for old is WITHDRAWN or FAILED
382 - }
383 -
384 - /**
385 - * Recompiles the specified intent.
386 - *
387 - * @param update intent update
388 - */
389 - // FIXME: update this to work
390 - private void executeRecompilingPhase(IntentUpdate update) {
391 - Intent intent = update.newIntent();
392 - // Indicate that the intent is entering the recompiling phase.
393 - store.setState(intent, RECOMPILING);
394 -
395 - List<FlowRuleBatchOperation> batches = Lists.newArrayList();
396 - try {
397 - // Compile the intent into installable derivatives.
398 - List<Intent> installable = compileIntent(intent, update);
399 -
400 - // If all went well, compare the existing list of installable
401 - // intents with the newly compiled list. If they are the same,
402 - // bail, out since the previous approach was determined not to
403 - // be viable.
404 - // FIXME do we need this?
405 - List<Intent> originalInstallable = store.getInstallableIntents(intent.id());
406 -
407 - //FIXME let's be smarter about how we perform the update
408 - //batches.addAll(uninstallIntent(intent, null));
409 -
410 - if (Objects.equals(originalInstallable, installable)) {
411 - eventDispatcher.post(store.setState(intent, FAILED));
412 - } else {
413 - // Otherwise, re-associate the newly compiled installable intents
414 - // with the top-level intent and kick off installing phase.
415 - store.setInstallableIntents(intent.id(), installable);
416 - // FIXME commented out for now
417 - //batches.addAll(executeInstallingPhase(update));
418 - }
419 - } catch (Exception e) {
420 - log.warn("Unable to recompile intent {} due to:", intent.id(), e);
421 -
422 - // If compilation failed, mark the intent as failed.
423 - eventDispatcher.post(store.setState(intent, FAILED));
424 - }
425 } 377 }
426 378
427 /** 379 /**
...@@ -442,9 +394,10 @@ public class IntentManager ...@@ -442,9 +394,10 @@ public class IntentManager
442 for (int i = 0; i < update.oldInstallables().size(); i++) { 394 for (int i = 0; i < update.oldInstallables().size(); i++) {
443 Intent oldInstallable = update.oldInstallables().get(i); 395 Intent oldInstallable = update.oldInstallables().get(i);
444 Intent newInstallable = update.newInstallables().get(i); 396 Intent newInstallable = update.newInstallables().get(i);
445 - if (oldInstallable.equals(newInstallable)) { 397 + //FIXME revisit this
446 - continue; 398 +// if (oldInstallable.equals(newInstallable)) {
447 - } 399 +// continue;
400 +// }
448 checkArgument(oldInstallable.getClass().equals(newInstallable.getClass()), 401 checkArgument(oldInstallable.getClass().equals(newInstallable.getClass()),
449 "Installable Intent type mismatch."); 402 "Installable Intent type mismatch.");
450 trackerService.removeTrackedResources(update.oldIntent().id(), oldInstallable.resources()); 403 trackerService.removeTrackedResources(update.oldIntent().id(), oldInstallable.resources());
...@@ -454,9 +407,12 @@ public class IntentManager ...@@ -454,9 +407,12 @@ public class IntentManager
454 } catch (IntentException e) { 407 } catch (IntentException e) {
455 log.warn("Unable to update intent {} due to:", update.oldIntent().id(), e); 408 log.warn("Unable to update intent {} due to:", update.oldIntent().id(), e);
456 //FIXME... we failed. need to uninstall (if same) or revert (if different) 409 //FIXME... we failed. need to uninstall (if same) or revert (if different)
410 + trackerService.removeTrackedResources(update.newIntent().id(), newInstallable.resources());
411 + update.setState(update.newIntent(), FAILED);
412 + batches = uninstallIntent(update.oldIntent(), update.oldInstallables());
457 } 413 }
458 } 414 }
459 - update.setBatches(batches); 415 + update.addBatches(batches);
460 } 416 }
461 417
462 /** 418 /**
...@@ -541,13 +497,12 @@ public class IntentManager ...@@ -541,13 +497,12 @@ public class IntentManager
541 } 497 }
542 498
543 /** 499 /**
544 - * TODO. 500 + * TODO. rename this...
545 - * @param op intent operation 501 + * @param update intent update
546 - * @return intent update
547 */ 502 */
548 - private IntentUpdate processIntentOperation(IntentOperation op) { 503 + private void processIntentUpdate(IntentUpdate update) {
549 - IntentUpdate update = new IntentUpdate(op);
550 504
505 + // check to see if the intent needs to be compiled or recompiled
551 if (update.newIntent() != null) { 506 if (update.newIntent() != null) {
552 executeCompilingPhase(update); 507 executeCompilingPhase(update);
553 } 508 }
...@@ -559,32 +514,29 @@ public class IntentManager ...@@ -559,32 +514,29 @@ public class IntentManager
559 } else if (update.oldInstallables() != null) { 514 } else if (update.oldInstallables() != null) {
560 executeWithdrawingPhase(update); 515 executeWithdrawingPhase(update);
561 } else { 516 } else {
562 - if (update.oldIntent() != null) { 517 + if (update.oldIntent() != null &&
563 - // TODO this shouldn't happen 518 + !update.oldIntent().equals(update.newIntent())) {
564 - return update; //FIXME 519 + // removing failed intent
565 - } 520 + update.setState(update.oldIntent(), WITHDRAWING);
566 - if (update.newIntent() != null) {
567 - // TODO assert that next state is failed
568 - return update; //FIXME
569 } 521 }
522 +// if (update.newIntent() != null) {
523 +// // TODO assert that next state is failed
524 +// }
570 } 525 }
571 -
572 - return update;
573 } 526 }
574 527
575 // TODO comments... 528 // TODO comments...
576 private class IntentUpdate { 529 private class IntentUpdate {
577 - private final IntentOperation op;
578 private final Intent oldIntent; 530 private final Intent oldIntent;
579 private final Intent newIntent; 531 private final Intent newIntent;
580 private final Map<Intent, IntentState> stateMap = Maps.newHashMap(); 532 private final Map<Intent, IntentState> stateMap = Maps.newHashMap();
581 533
582 private final List<Intent> oldInstallables; 534 private final List<Intent> oldInstallables;
583 private List<Intent> newInstallables; 535 private List<Intent> newInstallables;
584 - private List<FlowRuleBatchOperation> batches; 536 + private final List<FlowRuleBatchOperation> batches = Lists.newLinkedList();
537 + private int currentBatch = 0; // TODO: maybe replace with an iterator
585 538
586 IntentUpdate(IntentOperation op) { 539 IntentUpdate(IntentOperation op) {
587 - this.op = op;
588 switch (op.type()) { 540 switch (op.type()) {
589 case SUBMIT: 541 case SUBMIT:
590 newIntent = op.intent(); 542 newIntent = op.intent();
...@@ -600,7 +552,7 @@ public class IntentManager ...@@ -600,7 +552,7 @@ public class IntentManager
600 break; 552 break;
601 case UPDATE: 553 case UPDATE:
602 oldIntent = store.getIntent(op.intentId()); 554 oldIntent = store.getIntent(op.intentId());
603 - newIntent = oldIntent; //InnerAssignment: Inner assignments should be avoided. 555 + newIntent = oldIntent;
604 break; 556 break;
605 default: 557 default:
606 oldIntent = null; 558 oldIntent = null;
...@@ -617,7 +569,6 @@ public class IntentManager ...@@ -617,7 +569,6 @@ public class IntentManager
617 // fetch the old intent's installables from the store 569 // fetch the old intent's installables from the store
618 if (oldIntent != null) { 570 if (oldIntent != null) {
619 oldInstallables = store.getInstallableIntents(oldIntent.id()); 571 oldInstallables = store.getInstallableIntents(oldIntent.id());
620 - // TODO: remove intent from store after uninstall
621 } else { 572 } else {
622 oldInstallables = null; 573 oldInstallables = null;
623 } 574 }
...@@ -644,12 +595,72 @@ public class IntentManager ...@@ -644,12 +595,72 @@ public class IntentManager
644 store.setInstallableIntents(newIntent.id(), installables); 595 store.setInstallableIntents(newIntent.id(), installables);
645 } 596 }
646 597
598 + boolean isComplete() {
599 + return currentBatch >= batches.size();
600 + }
601 +
602 + FlowRuleBatchOperation currentBatch() {
603 + return !isComplete() ? batches.get(currentBatch) : null;
604 + }
605 +
606 + void incrementBatch(boolean success) {
607 + if (success) { // actually increment
608 + if (++currentBatch == batches.size()) {
609 + finalizeStates();
610 + }
611 + } else { // the current batch has failed, so recompile
612 + // remove the current batch and all remaining
613 + for (int i = currentBatch; i < batches.size(); i++) {
614 + batches.remove(i);
615 + }
616 + if (oldIntent != null) {
617 + executeWithdrawingPhase(this); // remove the old intent
618 + }
619 + if (newIntent != null) {
620 + setState(newIntent, FAILED);
621 + batches.addAll(uninstallIntent(newIntent, newInstallables()));
622 + }
623 +
624 + // FIXME: should we try to recompile?
625 + }
626 + }
627 +
628 + // FIXME make sure this is called!!!
629 + private void finalizeStates() {
630 + for (Intent intent : stateMap.keySet()) {
631 + switch (getState(intent)) {
632 + case INSTALLING:
633 + setState(intent, INSTALLED);
634 + break;
635 + case WITHDRAWING:
636 + setState(intent, WITHDRAWN);
637 + store.removeInstalledIntents(intent.id());
638 + //store.removeIntent(intent.id()); // FIXME we die a horrible death here
639 + break;
640 + case FAILED:
641 + store.removeInstalledIntents(intent.id());
642 + break;
643 +
644 + // FALLTHROUGH to default from here
645 + case SUBMITTED:
646 + case COMPILING:
647 + case RECOMPILING:
648 + case WITHDRAWN:
649 + case INSTALLED:
650 + default:
651 + //FIXME clean this up (we shouldn't ever get here)
652 + log.warn("Bad state: {} for {}", getState(intent), intent);
653 + break;
654 + }
655 + }
656 + }
657 +
647 List<FlowRuleBatchOperation> batches() { 658 List<FlowRuleBatchOperation> batches() {
648 return batches; 659 return batches;
649 } 660 }
650 661
651 - void setBatches(List<FlowRuleBatchOperation> batches) { 662 + void addBatches(List<FlowRuleBatchOperation> batches) {
652 - this.batches = batches; 663 + this.batches.addAll(batches);
653 } 664 }
654 665
655 IntentState getState(Intent intent) { 666 IntentState getState(Intent intent) {
...@@ -659,7 +670,7 @@ public class IntentManager ...@@ -659,7 +670,7 @@ public class IntentManager
659 void setState(Intent intent, IntentState newState) { 670 void setState(Intent intent, IntentState newState) {
660 // TODO: clean this up, or set to debug 671 // TODO: clean this up, or set to debug
661 IntentState oldState = stateMap.get(intent); 672 IntentState oldState = stateMap.get(intent);
662 - log.info("intent id: {}, old state: {}, new state: {}", 673 + log.debug("intent id: {}, old state: {}, new state: {}",
663 intent.id(), oldState, newState); 674 intent.id(), oldState, newState);
664 675
665 stateMap.put(intent, newState); 676 stateMap.put(intent, newState);
...@@ -674,143 +685,72 @@ public class IntentManager ...@@ -674,143 +685,72 @@ public class IntentManager
674 } 685 }
675 } 686 }
676 687
677 - private static List<FlowRuleBatchOperation> mergeBatches(Map<IntentOperation,
678 - IntentUpdate> intentUpdates) {
679 - //TODO test this.
680 - List<FlowRuleBatchOperation> batches = Lists.newArrayList();
681 - for (IntentUpdate update : intentUpdates.values()) {
682 - if (update.batches() == null) {
683 - continue;
684 - }
685 - int i = 0;
686 - for (FlowRuleBatchOperation batch : update.batches()) {
687 - if (i == batches.size()) {
688 - batches.add(batch);
689 - } else {
690 - FlowRuleBatchOperation existing = batches.get(i);
691 - existing.addAll(batch);
692 - }
693 - i++;
694 - }
695 - }
696 - return batches;
697 - }
698 -
699 - // Auxiliary runnable to perform asynchronous tasks.
700 - private class IntentTask implements Runnable {
701 - private final IntentOperations operations;
702 -
703 - public IntentTask(IntentOperations operations) {
704 - this.operations = operations;
705 - }
706 -
707 - @Override
708 - public void run() {
709 - Map<IntentOperation, IntentUpdate> intentUpdates = Maps.newHashMap();
710 - for (IntentOperation op : operations.operations()) {
711 - intentUpdates.put(op, processIntentOperation(op));
712 - }
713 - List<FlowRuleBatchOperation> batches = mergeBatches(intentUpdates);
714 - monitorExecutor.execute(new IntentInstallMonitor(operations, intentUpdates, batches));
715 - }
716 - }
717 -
718 private class IntentInstallMonitor implements Runnable { 688 private class IntentInstallMonitor implements Runnable {
719 689
720 private static final long TIMEOUT = 5000; // ms 690 private static final long TIMEOUT = 5000; // ms
691 + private static final int MAX_ATTEMPTS = 3;
692 +
721 private final IntentOperations ops; 693 private final IntentOperations ops;
722 - private final Map<IntentOperation, IntentUpdate> intentUpdateMap; 694 + private final List<IntentUpdate> intentUpdates = Lists.newArrayList();
723 - private final List<FlowRuleBatchOperation> work; 695 +
724 private Future<CompletedBatchOperation> future; 696 private Future<CompletedBatchOperation> future;
725 - private final long startTime = System.currentTimeMillis(); 697 + private long startTime = System.currentTimeMillis();
726 - private final long endTime = startTime + TIMEOUT; 698 + private long endTime = startTime + TIMEOUT;
699 + private int installAttempt;
727 700
728 - public IntentInstallMonitor(IntentOperations ops, 701 + public IntentInstallMonitor(IntentOperations ops) {
729 - Map<IntentOperation, IntentUpdate> intentUpdateMap,
730 - List<FlowRuleBatchOperation> work) {
731 this.ops = ops; 702 this.ops = ops;
732 - this.intentUpdateMap = intentUpdateMap; 703 + }
733 - this.work = work; 704 +
705 + private void buildIntentUpdates() {
706 + for (IntentOperation op : ops.operations()) {
707 + IntentUpdate update = new IntentUpdate(op);
708 + intentUpdates.add(update);
709 + processIntentUpdate(update);
710 + }
734 future = applyNextBatch(); 711 future = applyNextBatch();
735 } 712 }
736 713
737 /** 714 /**
738 - * Applies the next batch, and returns the future. 715 + * Builds and applies the next batch, and returns the future.
739 * 716 *
740 * @return Future for next batch 717 * @return Future for next batch
741 */ 718 */
742 private Future<CompletedBatchOperation> applyNextBatch() { 719 private Future<CompletedBatchOperation> applyNextBatch() {
743 - if (work.isEmpty()) { 720 + //TODO test this. (also, maybe save this batch)
744 - return null; 721 + FlowRuleBatchOperation batch = new FlowRuleBatchOperation(Collections.emptyList());
722 + for (IntentUpdate update : intentUpdates) {
723 + if (!update.isComplete()) {
724 + batch.addAll(update.currentBatch());
725 + }
745 } 726 }
746 - FlowRuleBatchOperation batch = work.remove(0); 727 + return (batch.size() > 0) ? flowRuleService.applyBatch(batch) : null;
747 - return flowRuleService.applyBatch(batch);
748 } 728 }
749 729
750 - /** 730 + private void updateBatches(CompletedBatchOperation completed) {
751 - * Update the intent store with the next status for this intent. 731 + if (completed.isSuccess()) {
752 - */ 732 + for (IntentUpdate update : intentUpdates) {
753 - private void updateIntents() { 733 + update.incrementBatch(true);
754 - // FIXME we assume everything passes for now.
755 - for (IntentUpdate update : intentUpdateMap.values()) {
756 - for (Intent intent : update.stateMap().keySet()) {
757 - switch (update.getState(intent)) {
758 - case INSTALLING:
759 - update.setState(intent, INSTALLED);
760 - break;
761 - case WITHDRAWING:
762 - update.setState(intent, WITHDRAWN);
763 - // Fall-through
764 - case FAILED:
765 - store.removeInstalledIntents(intent.id());
766 - break;
767 -
768 - case SUBMITTED:
769 - case COMPILING:
770 - case RECOMPILING:
771 - case WITHDRAWN:
772 - case INSTALLED:
773 - default:
774 - //FIXME clean this up (we shouldn't ever get here)
775 - log.warn("Bad state: {} for {}", update.getState(intent), intent);
776 - break;
777 - }
778 } 734 }
779 - } 735 + } else {
780 - /* 736 + // entire batch has been reverted...
781 - for (IntentOperation op : ops.operations()) { 737 + log.warn("Failed items: {}", completed.failedItems());
782 - switch (op.type()) { 738 +
783 - case SUBMIT: 739 + for (Long id : completed.failedIds()) {
784 - store.setState(op.intent(), INSTALLED); 740 + IntentId targetId = IntentId.valueOf(id);
785 - break; 741 + for (IntentUpdate update : intentUpdates) {
786 - case WITHDRAW: 742 + List<Intent> installables = Lists.newArrayList(update.newInstallables());
787 - Intent intent = store.getIntent(op.intentId()); 743 + installables.addAll(update.oldInstallables());
788 - store.setState(intent, WITHDRAWN); 744 + for (Intent intent : installables) {
789 - break; 745 + if (intent.id().equals(targetId)) {
790 - case REPLACE: 746 + update.incrementBatch(false);
791 - store.setState(op.intent(), INSTALLED); 747 + break;
792 - intent = store.getIntent(op.intentId()); 748 + }
793 - store.setState(intent, WITHDRAWN); 749 + }
794 - break; 750 + }
795 - case UPDATE: 751 + // don't increment the non-failed items, as they have been reverted.
796 - intent = store.getIntent(op.intentId());
797 - store.setState(intent, INSTALLED);
798 - break;
799 - default:
800 - break;
801 } 752 }
802 } 753 }
803 - */
804 - /*
805 - if (nextState == RECOMPILING) {
806 - eventDispatcher.post(store.setState(intent, FAILED));
807 - // FIXME try to recompile
808 -// executor.execute(new IntentTask(nextState, intent));
809 - } else if (nextState == INSTALLED || nextState == WITHDRAWN) {
810 - eventDispatcher.post(store.setState(intent, nextState));
811 - } else {
812 - log.warn("Invalid next intent state {} for intent {}", nextState, intent);
813 - }*/
814 } 754 }
815 755
816 /** 756 /**
...@@ -822,34 +762,60 @@ public class IntentManager ...@@ -822,34 +762,60 @@ public class IntentManager
822 } 762 }
823 try { 763 try {
824 CompletedBatchOperation completed = future.get(100, TimeUnit.NANOSECONDS); 764 CompletedBatchOperation completed = future.get(100, TimeUnit.NANOSECONDS);
825 - if (completed.isSuccess()) { 765 + updateBatches(completed);
826 - future = applyNextBatch(); 766 + future = applyNextBatch();
827 - } else {
828 - // TODO check if future succeeded and if not report fail items
829 - log.warn("Failed items: {}", completed.failedItems());
830 - // FIXME revert.... by submitting a new batch
831 - //uninstallIntent(intent, RECOMPILING);
832 - }
833 } catch (TimeoutException | InterruptedException | ExecutionException te) { 767 } catch (TimeoutException | InterruptedException | ExecutionException te) {
834 //TODO look into error message 768 //TODO look into error message
835 - log.debug("Intallations of intent {} is still pending", ops); 769 + log.debug("Installation of intents are still pending: {}", ops);
836 } 770 }
837 } 771 }
838 772
773 + private void retry() {
774 + if (future.cancel(true)) { // cancel success; batch is reverted
775 + // reset the timer
776 + endTime = System.currentTimeMillis() + TIMEOUT;
777 + if (installAttempt++ >= MAX_ATTEMPTS) {
778 + log.warn("Install request timed out: {}", ops);
779 + for (IntentUpdate update : intentUpdates) {
780 + update.incrementBatch(false);
781 + }
782 + } // else just resubmit the work
783 + future = applyNextBatch();
784 + monitorExecutor.submit(this);
785 + } else {
786 + // FIXME
787 + // cancel failed... batch is broken; shouldn't happen!
788 + // we could manually reverse everything
789 + // ... or just core dump and send email to Ali
790 + batchService.removeIntentOperations(ops);
791 + }
792 + }
793 +
794 + boolean isComplete() {
795 + // TODO: actually check with the intent update?
796 + return future == null;
797 + }
798 +
839 @Override 799 @Override
840 public void run() { 800 public void run() {
841 - processFutures(); 801 + try {
842 - if (future == null) { 802 + if (intentUpdates.isEmpty()) {
843 - // woohoo! we are done! 803 + // this should only be called on the first iteration
844 - updateIntents(); 804 + // note: this a "expensive", so it is not done in the constructor
845 - batchService.removeIntentOperations(ops); 805 + buildIntentUpdates();
846 - } else if (endTime < System.currentTimeMillis()) { 806 + }
847 - log.warn("Install request timed out"); 807 + processFutures();
848 -// future.cancel(true); 808 + if (isComplete()) {
849 - // TODO retry and/or report the failure 809 + // there are no outstanding batches; we are done
850 - } else { 810 + batchService.removeIntentOperations(ops);
851 - // resubmit ourselves if we are not done yet 811 + } else if (endTime < System.currentTimeMillis()) {
852 - monitorExecutor.submit(this); 812 + retry();
813 + } else {
814 + // we are not done yet, yield the thread by resubmitting ourselves
815 + monitorExecutor.submit(this);
816 + }
817 + } catch (Exception e) {
818 + log.error("Error submitting batches:", e);
853 } 819 }
854 } 820 }
855 } 821 }
...@@ -859,7 +825,7 @@ public class IntentManager ...@@ -859,7 +825,7 @@ public class IntentManager
859 public void execute(IntentOperations operations) { 825 public void execute(IntentOperations operations) {
860 log.info("Execute operations: {}", operations); 826 log.info("Execute operations: {}", operations);
861 //FIXME: perhaps we want to track this task so that we can cancel it. 827 //FIXME: perhaps we want to track this task so that we can cancel it.
862 - executor.execute(new IntentTask(operations)); 828 + monitorExecutor.execute(new IntentInstallMonitor(operations));
863 } 829 }
864 830
865 @Override 831 @Override
......
...@@ -101,7 +101,8 @@ public class PathIntentInstaller implements IntentInstaller<PathIntent> { ...@@ -101,7 +101,8 @@ public class PathIntentInstaller implements IntentInstaller<PathIntent> {
101 FlowRule rule = new DefaultFlowRule(link.src().deviceId(), 101 FlowRule rule = new DefaultFlowRule(link.src().deviceId(),
102 builder.build(), treatment, 123, //FIXME 123 102 builder.build(), treatment, 123, //FIXME 123
103 appId, (short) (intent.id().fingerprint() & 0xffff), 0, true); 103 appId, (short) (intent.id().fingerprint() & 0xffff), 0, true);
104 - rules.add(new FlowRuleBatchEntry(FlowRuleOperation.ADD, rule)); 104 + rules.add(new FlowRuleBatchEntry(FlowRuleOperation.ADD, rule,
105 + intent.id().fingerprint()));
105 prev = link.dst(); 106 prev = link.dst();
106 } 107 }
107 return Lists.newArrayList(new FlowRuleBatchOperation(rules)); 108 return Lists.newArrayList(new FlowRuleBatchOperation(rules));
...@@ -127,7 +128,8 @@ public class PathIntentInstaller implements IntentInstaller<PathIntent> { ...@@ -127,7 +128,8 @@ public class PathIntentInstaller implements IntentInstaller<PathIntent> {
127 FlowRule rule = new DefaultFlowRule(link.src().deviceId(), 128 FlowRule rule = new DefaultFlowRule(link.src().deviceId(),
128 builder.build(), treatment, 129 builder.build(), treatment,
129 123, appId, (short) (intent.id().fingerprint() & 0xffff), 0, true); 130 123, appId, (short) (intent.id().fingerprint() & 0xffff), 0, true);
130 - rules.add(new FlowRuleBatchEntry(FlowRuleOperation.REMOVE, rule)); 131 + rules.add(new FlowRuleBatchEntry(FlowRuleOperation.REMOVE, rule,
132 + intent.id().fingerprint()));
131 prev = link.dst(); 133 prev = link.dst();
132 } 134 }
133 return Lists.newArrayList(new FlowRuleBatchOperation(rules)); 135 return Lists.newArrayList(new FlowRuleBatchOperation(rules));
......
1 +package org.onlab.onos.net.intent.impl;
2 +
3 +import com.google.common.collect.HashMultimap;
4 +import com.google.common.collect.Lists;
5 +import com.google.common.collect.Maps;
6 +import com.google.common.collect.Multimap;
7 +import com.google.common.collect.Sets;
8 +import org.hamcrest.Description;
9 +import org.hamcrest.Matchers;
10 +import org.hamcrest.TypeSafeMatcher;
11 +import org.junit.After;
12 +import org.junit.Before;
13 +import org.junit.Test;
14 +import org.onlab.onos.TestApplicationId;
15 +import org.onlab.onos.core.ApplicationId;
16 +import org.onlab.onos.event.impl.TestEventDispatcher;
17 +import org.onlab.onos.net.NetworkResource;
18 +import org.onlab.onos.net.flow.FlowRule;
19 +import org.onlab.onos.net.flow.FlowRuleBatchEntry;
20 +import org.onlab.onos.net.flow.FlowRuleBatchEntry.FlowRuleOperation;
21 +import org.onlab.onos.net.flow.FlowRuleBatchOperation;
22 +import org.onlab.onos.net.intent.Intent;
23 +import org.onlab.onos.net.intent.IntentCompiler;
24 +import org.onlab.onos.net.intent.IntentEvent;
25 +import org.onlab.onos.net.intent.IntentEvent.Type;
26 +import org.onlab.onos.net.intent.IntentExtensionService;
27 +import org.onlab.onos.net.intent.IntentId;
28 +import org.onlab.onos.net.intent.IntentInstaller;
29 +import org.onlab.onos.net.intent.IntentListener;
30 +import org.onlab.onos.net.intent.IntentService;
31 +import org.onlab.onos.net.intent.IntentState;
32 +import org.onlab.onos.net.intent.IntentTestsMocks;
33 +import org.onlab.onos.net.resource.LinkResourceAllocations;
34 +import org.onlab.onos.store.trivial.impl.SimpleIntentBatchQueue;
35 +import org.onlab.onos.store.trivial.impl.SimpleIntentStore;
36 +
37 +import java.util.Collection;
38 +import java.util.List;
39 +import java.util.Map;
40 +import java.util.Set;
41 +import java.util.concurrent.CountDownLatch;
42 +import java.util.concurrent.atomic.AtomicLong;
43 +
44 +import static org.hamcrest.Matchers.equalTo;
45 +import static org.hamcrest.Matchers.hasItem;
46 +import static org.junit.Assert.assertEquals;
47 +import static org.junit.Assert.assertTrue;
48 +import static org.onlab.onos.net.intent.IntentState.*;
49 +import static org.onlab.util.Tools.delay;
50 +
51 +/**
52 + * Test intent manager and transitions.
53 + *
54 + * TODO implement the following tests:
55 + * - {submit, withdraw, update, replace} intent
56 + * - {submit, update, recomiling} intent with failed compilation
57 + * - failed reservation
58 + * - push timeout recovery
59 + * - failed items recovery
60 + *
61 + * in general, verify intents store, flow store, and work queue
62 + */
63 +public class IntentManagerTest {
64 +
65 + private static final ApplicationId APPID = new TestApplicationId("manager-test");
66 +
67 + private IntentManager manager;
68 + private MockFlowRuleService flowRuleService;
69 +
70 + protected IntentService service;
71 + protected IntentExtensionService extensionService;
72 + protected TestListener listener = new TestListener();
73 + protected TestIntentCompiler compiler = new TestIntentCompiler();
74 + protected TestIntentInstaller installer = new TestIntentInstaller();
75 +
76 + @Before
77 + public void setUp() {
78 + manager = new IntentManager();
79 + flowRuleService = new MockFlowRuleService();
80 + manager.store = new SimpleIntentStore();
81 + manager.batchService = new SimpleIntentBatchQueue();
82 + manager.eventDispatcher = new TestEventDispatcher();
83 + manager.trackerService = new TestIntentTracker();
84 + manager.flowRuleService = flowRuleService;
85 + service = manager;
86 + extensionService = manager;
87 +
88 + manager.activate();
89 + service.addListener(listener);
90 + extensionService.registerCompiler(MockIntent.class, compiler);
91 + extensionService.registerInstaller(MockInstallableIntent.class, installer);
92 +
93 + assertTrue("store should be empty",
94 + Sets.newHashSet(service.getIntents()).isEmpty());
95 + assertEquals(0L, flowRuleService.getFlowRuleCount());
96 + }
97 +
98 + @After
99 + public void tearDown() {
100 + // verify that all intents are parked and the batch operation is unblocked
101 + Set<IntentState> parked = Sets.newHashSet(INSTALLED, WITHDRAWN, FAILED);
102 + for (Intent i : service.getIntents()) {
103 + IntentState state = service.getIntentState(i.id());
104 + assertTrue("Intent " + i.id() + " is in invalid state " + state,
105 + parked.contains(state));
106 + }
107 + //the batch has not yet been removed when we receive the last event
108 + // FIXME: this doesn't guarantee to avoid the race
109 + for (int tries = 0; tries < 10; tries++) {
110 + if (manager.batchService.getPendingOperations().isEmpty() &&
111 + manager.batchService.getCurrentOperations().isEmpty()) {
112 + break;
113 + }
114 + delay(10);
115 + }
116 + assertTrue("There are still pending batch operations.",
117 + manager.batchService.getPendingOperations().isEmpty());
118 + assertTrue("There are still outstanding batch operations.",
119 + manager.batchService.getCurrentOperations().isEmpty());
120 +
121 + extensionService.unregisterCompiler(MockIntent.class);
122 + extensionService.unregisterInstaller(MockInstallableIntent.class);
123 + service.removeListener(listener);
124 + manager.deactivate();
125 + // TODO null the other refs?
126 + }
127 +
128 + @Test
129 + public void submitIntent() {
130 + flowRuleService.setFuture(true);
131 +
132 + listener.setLatch(1, Type.SUBMITTED);
133 + listener.setLatch(1, Type.INSTALLED);
134 + Intent intent = new MockIntent(MockIntent.nextId());
135 + service.submit(intent);
136 + listener.await(Type.SUBMITTED);
137 + listener.await(Type.INSTALLED);
138 + assertEquals(1L, service.getIntentCount());
139 + assertEquals(1L, flowRuleService.getFlowRuleCount());
140 + }
141 +
142 + @Test
143 + public void withdrawIntent() {
144 + flowRuleService.setFuture(true);
145 +
146 + listener.setLatch(1, Type.INSTALLED);
147 + Intent intent = new MockIntent(MockIntent.nextId());
148 + service.submit(intent);
149 + listener.await(Type.INSTALLED);
150 + assertEquals(1L, service.getIntentCount());
151 + assertEquals(1L, flowRuleService.getFlowRuleCount());
152 +
153 + listener.setLatch(1, Type.WITHDRAWN);
154 + service.withdraw(intent);
155 + listener.await(Type.WITHDRAWN);
156 + assertEquals(1L, service.getIntentCount());
157 + assertEquals(0L, flowRuleService.getFlowRuleCount());
158 + }
159 +
160 + @Test
161 + public void stressSubmitWithdraw() {
162 + flowRuleService.setFuture(true);
163 +
164 + int count = 500;
165 +
166 + listener.setLatch(count, Type.INSTALLED);
167 + listener.setLatch(count, Type.WITHDRAWN);
168 +
169 + Intent intent = new MockIntent(MockIntent.nextId());
170 + for (int i = 0; i < count; i++) {
171 + service.submit(intent);
172 + service.withdraw(intent);
173 + }
174 +
175 + listener.await(Type.INSTALLED);
176 + listener.await(Type.WITHDRAWN);
177 + assertEquals(1L, service.getIntentCount());
178 + assertEquals(0L, flowRuleService.getFlowRuleCount());
179 + }
180 +
181 + @Test
182 + public void replaceIntent() {
183 + flowRuleService.setFuture(true);
184 +
185 + MockIntent intent = new MockIntent(MockIntent.nextId());
186 + listener.setLatch(1, Type.INSTALLED);
187 + service.submit(intent);
188 + listener.await(Type.INSTALLED);
189 + assertEquals(1L, service.getIntentCount());
190 + assertEquals(1L, manager.flowRuleService.getFlowRuleCount());
191 +
192 + MockIntent intent2 = new MockIntent(MockIntent.nextId());
193 + listener.setLatch(1, Type.WITHDRAWN);
194 + listener.setLatch(1, Type.SUBMITTED);
195 + listener.setLatch(1, Type.INSTALLED);
196 + service.replace(intent.id(), intent2);
197 + listener.await(Type.WITHDRAWN);
198 + listener.await(Type.INSTALLED);
199 + assertEquals(2L, service.getIntentCount());
200 + assertEquals(1L, manager.flowRuleService.getFlowRuleCount());
201 + assertEquals(intent2.number().intValue(),
202 + flowRuleService.flows.iterator().next().priority());
203 + }
204 +
205 + private static class TestListener implements IntentListener {
206 + final Multimap<IntentEvent.Type, IntentEvent> events = HashMultimap.create();
207 + Map<IntentEvent.Type, CountDownLatch> latchMap = Maps.newHashMap();
208 +
209 + @Override
210 + public void event(IntentEvent event) {
211 + events.put(event.type(), event);
212 + if (latchMap.containsKey(event.type())) {
213 + latchMap.get(event.type()).countDown();
214 + }
215 + }
216 +
217 + public int getCounts(IntentEvent.Type type) {
218 + return events.get(type).size();
219 + }
220 +
221 + public void setLatch(int count, IntentEvent.Type type) {
222 + latchMap.put(type, new CountDownLatch(count));
223 + }
224 +
225 + public void await(IntentEvent.Type type) {
226 + try {
227 + latchMap.get(type).await();
228 + } catch (InterruptedException e) {
229 + e.printStackTrace();
230 + }
231 + }
232 + }
233 +
234 + private static class TestIntentTracker implements ObjectiveTrackerService {
235 + private TopologyChangeDelegate delegate;
236 + @Override
237 + public void setDelegate(TopologyChangeDelegate delegate) {
238 + this.delegate = delegate;
239 + }
240 +
241 + @Override
242 + public void unsetDelegate(TopologyChangeDelegate delegate) {
243 + if (delegate.equals(this.delegate)) {
244 + this.delegate = null;
245 + }
246 + }
247 +
248 + @Override
249 + public void addTrackedResources(IntentId intentId, Collection<NetworkResource> resources) {
250 + //TODO
251 + }
252 +
253 + @Override
254 + public void removeTrackedResources(IntentId intentId, Collection<NetworkResource> resources) {
255 + //TODO
256 + }
257 + }
258 +
259 + private static class MockIntent extends Intent {
260 + private static AtomicLong counter = new AtomicLong(0);
261 +
262 + private final Long number;
263 + // Nothing new here
264 + public MockIntent(Long number) {
265 + super(id(MockIntent.class, number), APPID, null);
266 + this.number = number;
267 + }
268 +
269 + public Long number() {
270 + return number;
271 + }
272 +
273 + public static Long nextId() {
274 + return counter.getAndIncrement();
275 + }
276 + }
277 +
278 + private static class MockInstallableIntent extends MockIntent {
279 + public MockInstallableIntent(Long number) {
280 + super(number);
281 + }
282 +
283 + @Override
284 + public boolean isInstallable() {
285 + return true;
286 + }
287 + }
288 +
289 + private static class TestIntentCompiler implements IntentCompiler<MockIntent> {
290 + @Override
291 + public List<Intent> compile(MockIntent intent, List<Intent> installable,
292 + Set<LinkResourceAllocations> resources) {
293 + return Lists.newArrayList(new MockInstallableIntent(intent.number()));
294 + }
295 + }
296 +
297 + private static class TestIntentInstaller implements IntentInstaller<MockInstallableIntent> {
298 + @Override
299 + public List<FlowRuleBatchOperation> install(MockInstallableIntent intent) {
300 + FlowRule fr = new IntentTestsMocks.MockFlowRule(intent.number().intValue());
301 + List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
302 + rules.add(new FlowRuleBatchEntry(FlowRuleOperation.ADD, fr));
303 + return Lists.newArrayList(new FlowRuleBatchOperation(rules));
304 + }
305 +
306 + @Override
307 + public List<FlowRuleBatchOperation> uninstall(MockInstallableIntent intent) {
308 + FlowRule fr = new IntentTestsMocks.MockFlowRule(intent.number().intValue());
309 + List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
310 + rules.add(new FlowRuleBatchEntry(FlowRuleOperation.REMOVE, fr));
311 + return Lists.newArrayList(new FlowRuleBatchOperation(rules));
312 + }
313 +
314 + @Override
315 + public List<FlowRuleBatchOperation> replace(MockInstallableIntent oldIntent, MockInstallableIntent newIntent) {
316 + FlowRule fr = new IntentTestsMocks.MockFlowRule(oldIntent.number().intValue());
317 + FlowRule fr2 = new IntentTestsMocks.MockFlowRule(newIntent.number().intValue());
318 + List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
319 + rules.add(new FlowRuleBatchEntry(FlowRuleOperation.REMOVE, fr));
320 + rules.add(new FlowRuleBatchEntry(FlowRuleOperation.ADD, fr2));
321 + return Lists.newArrayList(new FlowRuleBatchOperation(rules));
322 + }
323 + }
324 +
325 + /**
326 + * Hamcrest matcher to check that a conllection of Intents contains an
327 + * Intent with the specified Intent Id.
328 + */
329 + public static class EntryForIntentMatcher extends TypeSafeMatcher<Collection<Intent>> {
330 + private final String id;
331 +
332 + public EntryForIntentMatcher(String idValue) {
333 + id = idValue;
334 + }
335 +
336 + @Override
337 + public boolean matchesSafely(Collection<Intent> intents) {
338 + return hasItem(Matchers.<Intent>hasProperty("id", equalTo(id))).matches(intents);
339 + }
340 +
341 + @Override
342 + public void describeTo(Description description) {
343 + description.appendText("an intent with id \" ").
344 + appendText(id).
345 + appendText("\"");
346 + }
347 + }
348 +}
1 +package org.onlab.onos.net.intent.impl;
2 +
3 +import com.google.common.collect.Sets;
4 +import com.google.common.util.concurrent.Futures;
5 +import org.onlab.onos.core.ApplicationId;
6 +import org.onlab.onos.net.DeviceId;
7 +import org.onlab.onos.net.flow.CompletedBatchOperation;
8 +import org.onlab.onos.net.flow.FlowEntry;
9 +import org.onlab.onos.net.flow.FlowRule;
10 +import org.onlab.onos.net.flow.FlowRuleBatchEntry;
11 +import org.onlab.onos.net.flow.FlowRuleBatchOperation;
12 +import org.onlab.onos.net.flow.FlowRuleListener;
13 +import org.onlab.onos.net.flow.FlowRuleService;
14 +
15 +import java.util.Collections;
16 +import java.util.Set;
17 +import java.util.concurrent.Future;
18 +
19 +
20 +public class MockFlowRuleService implements FlowRuleService {
21 +
22 + private Future<CompletedBatchOperation> future;
23 + final Set<FlowRule> flows = Sets.newHashSet();
24 +
25 + public void setFuture(boolean success) {
26 + future = Futures.immediateFuture(new CompletedBatchOperation(true, Collections.emptySet()));
27 + }
28 +
29 + @Override
30 + public Future<CompletedBatchOperation> applyBatch(FlowRuleBatchOperation batch) {
31 + for (FlowRuleBatchEntry fbe : batch.getOperations()) {
32 + FlowRule fr = fbe.getTarget();
33 + switch (fbe.getOperator()) {
34 + case ADD:
35 + flows.add(fr);
36 + break;
37 + case REMOVE:
38 + flows.remove(fr);
39 + break;
40 + case MODIFY:
41 + break;
42 + default:
43 + break;
44 + }
45 + }
46 + return future;
47 + }
48 +
49 + @Override
50 + public int getFlowRuleCount() {
51 + return flows.size();
52 + }
53 +
54 + @Override
55 + public Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
56 + return null;
57 + }
58 +
59 + @Override
60 + public void applyFlowRules(FlowRule... flowRules) {
61 + }
62 +
63 + @Override
64 + public void removeFlowRules(FlowRule... flowRules) {
65 + }
66 +
67 + @Override
68 + public void removeFlowRulesById(ApplicationId appId) {
69 + }
70 +
71 + @Override
72 + public Iterable<FlowRule> getFlowRulesById(ApplicationId id) {
73 + return null;
74 + }
75 +
76 + @Override
77 + public Iterable<FlowRule> getFlowRulesByGroupId(ApplicationId appId, short groupId) {
78 + return null;
79 + }
80 +
81 + @Override
82 + public void addListener(FlowRuleListener listener) {
83 +
84 + }
85 +
86 + @Override
87 + public void removeListener(FlowRuleListener listener) {
88 +
89 + }
90 +}
91 +
...@@ -340,7 +340,8 @@ public class DistributedFlowRuleStore ...@@ -340,7 +340,8 @@ public class DistributedFlowRuleStore
340 public Future<CompletedBatchOperation> storeBatch(FlowRuleBatchOperation operation) { 340 public Future<CompletedBatchOperation> storeBatch(FlowRuleBatchOperation operation) {
341 341
342 if (operation.getOperations().isEmpty()) { 342 if (operation.getOperations().isEmpty()) {
343 - return Futures.immediateFuture(new CompletedBatchOperation(true, Collections.<FlowRule>emptySet())); 343 + return Futures.immediateFuture(new CompletedBatchOperation(true,
344 + Collections.<FlowRule>emptySet()));
344 } 345 }
345 346
346 DeviceId deviceId = operation.getOperations().get(0).getTarget().deviceId(); 347 DeviceId deviceId = operation.getOperations().get(0).getTarget().deviceId();
...@@ -379,8 +380,8 @@ public class DistributedFlowRuleStore ...@@ -379,8 +380,8 @@ public class DistributedFlowRuleStore
379 private ListenableFuture<CompletedBatchOperation> 380 private ListenableFuture<CompletedBatchOperation>
380 storeBatchInternal(FlowRuleBatchOperation operation) { 381 storeBatchInternal(FlowRuleBatchOperation operation) {
381 382
382 - final List<StoredFlowEntry> toRemove = new ArrayList<>(); 383 + final List<FlowRuleBatchEntry> toRemove = new ArrayList<>();
383 - final List<StoredFlowEntry> toAdd = new ArrayList<>(); 384 + final List<FlowRuleBatchEntry> toAdd = new ArrayList<>();
384 DeviceId did = null; 385 DeviceId did = null;
385 386
386 387
...@@ -396,14 +397,14 @@ public class DistributedFlowRuleStore ...@@ -396,14 +397,14 @@ public class DistributedFlowRuleStore
396 StoredFlowEntry entry = getFlowEntryInternal(flowRule); 397 StoredFlowEntry entry = getFlowEntryInternal(flowRule);
397 if (entry != null) { 398 if (entry != null) {
398 entry.setState(FlowEntryState.PENDING_REMOVE); 399 entry.setState(FlowEntryState.PENDING_REMOVE);
399 - toRemove.add(entry); 400 + toRemove.add(batchEntry);
400 } 401 }
401 } else if (op.equals(FlowRuleOperation.ADD)) { 402 } else if (op.equals(FlowRuleOperation.ADD)) {
402 StoredFlowEntry flowEntry = new DefaultFlowEntry(flowRule); 403 StoredFlowEntry flowEntry = new DefaultFlowEntry(flowRule);
403 DeviceId deviceId = flowRule.deviceId(); 404 DeviceId deviceId = flowRule.deviceId();
404 if (!flowEntries.containsEntry(deviceId, flowEntry)) { 405 if (!flowEntries.containsEntry(deviceId, flowEntry)) {
405 flowEntries.put(deviceId, flowEntry); 406 flowEntries.put(deviceId, flowEntry);
406 - toAdd.add(flowEntry); 407 + toAdd.add(batchEntry);
407 } 408 }
408 } 409 }
409 } 410 }
...@@ -427,8 +428,8 @@ public class DistributedFlowRuleStore ...@@ -427,8 +428,8 @@ public class DistributedFlowRuleStore
427 } 428 }
428 429
429 private void updateBackup(final DeviceId deviceId, 430 private void updateBackup(final DeviceId deviceId,
430 - final List<StoredFlowEntry> toAdd, 431 + final List<FlowRuleBatchEntry> toAdd,
431 - final List<? extends FlowRule> list) { 432 + final List<FlowRuleBatchEntry> list) {
432 433
433 Future<?> submit = backupExecutors.submit(new UpdateBackup(deviceId, toAdd, list)); 434 Future<?> submit = backupExecutors.submit(new UpdateBackup(deviceId, toAdd, list));
434 435
...@@ -442,8 +443,9 @@ public class DistributedFlowRuleStore ...@@ -442,8 +443,9 @@ public class DistributedFlowRuleStore
442 } 443 }
443 } 444 }
444 445
445 - private void updateBackup(DeviceId deviceId, List<StoredFlowEntry> toAdd) { 446 + private void updateBackup(DeviceId deviceId, List<FlowRuleBatchEntry> toAdd) {
446 - updateBackup(deviceId, toAdd, Collections.<FlowEntry>emptyList()); 447 +
448 + updateBackup(deviceId, toAdd, Collections.<FlowRuleBatchEntry>emptyList());
447 } 449 }
448 450
449 @Override 451 @Override
...@@ -477,8 +479,9 @@ public class DistributedFlowRuleStore ...@@ -477,8 +479,9 @@ public class DistributedFlowRuleStore
477 stored.setPackets(rule.packets()); 479 stored.setPackets(rule.packets());
478 if (stored.state() == FlowEntryState.PENDING_ADD) { 480 if (stored.state() == FlowEntryState.PENDING_ADD) {
479 stored.setState(FlowEntryState.ADDED); 481 stored.setState(FlowEntryState.ADDED);
480 - // update backup. 482 + FlowRuleBatchEntry entry =
481 - updateBackup(did, Arrays.asList(stored)); 483 + new FlowRuleBatchEntry(FlowRuleOperation.ADD, stored);
484 + updateBackup(did, Arrays.asList(entry));
482 return new FlowRuleEvent(Type.RULE_ADDED, rule); 485 return new FlowRuleEvent(Type.RULE_ADDED, rule);
483 } 486 }
484 return new FlowRuleEvent(Type.RULE_UPDATED, rule); 487 return new FlowRuleEvent(Type.RULE_UPDATED, rule);
...@@ -515,7 +518,9 @@ public class DistributedFlowRuleStore ...@@ -515,7 +518,9 @@ public class DistributedFlowRuleStore
515 try { 518 try {
516 // This is where one could mark a rule as removed and still keep it in the store. 519 // This is where one could mark a rule as removed and still keep it in the store.
517 final boolean removed = flowEntries.remove(deviceId, rule); 520 final boolean removed = flowEntries.remove(deviceId, rule);
518 - updateBackup(deviceId, Collections.<StoredFlowEntry>emptyList(), Arrays.asList(rule)); 521 + FlowRuleBatchEntry entry =
522 + new FlowRuleBatchEntry(FlowRuleOperation.REMOVE, rule);
523 + updateBackup(deviceId, Collections.<FlowRuleBatchEntry>emptyList(), Arrays.asList(entry));
519 if (removed) { 524 if (removed) {
520 return new FlowRuleEvent(RULE_REMOVED, rule); 525 return new FlowRuleEvent(RULE_REMOVED, rule);
521 } else { 526 } else {
...@@ -687,15 +692,17 @@ public class DistributedFlowRuleStore ...@@ -687,15 +692,17 @@ public class DistributedFlowRuleStore
687 } 692 }
688 693
689 // Task to update FlowEntries in backup HZ store 694 // Task to update FlowEntries in backup HZ store
695 + // TODO: Should be refactored to contain only one list and not
696 + // toAdd and toRemove
690 private final class UpdateBackup implements Runnable { 697 private final class UpdateBackup implements Runnable {
691 698
692 private final DeviceId deviceId; 699 private final DeviceId deviceId;
693 - private final List<StoredFlowEntry> toAdd; 700 + private final List<FlowRuleBatchEntry> toAdd;
694 - private final List<? extends FlowRule> toRemove; 701 + private final List<FlowRuleBatchEntry> toRemove;
695 702
696 public UpdateBackup(DeviceId deviceId, 703 public UpdateBackup(DeviceId deviceId,
697 - List<StoredFlowEntry> toAdd, 704 + List<FlowRuleBatchEntry> toAdd,
698 - List<? extends FlowRule> list) { 705 + List<FlowRuleBatchEntry> list) {
699 this.deviceId = checkNotNull(deviceId); 706 this.deviceId = checkNotNull(deviceId);
700 this.toAdd = checkNotNull(toAdd); 707 this.toAdd = checkNotNull(toAdd);
701 this.toRemove = checkNotNull(list); 708 this.toRemove = checkNotNull(list);
...@@ -707,7 +714,8 @@ public class DistributedFlowRuleStore ...@@ -707,7 +714,8 @@ public class DistributedFlowRuleStore
707 log.trace("update backup {} +{} -{}", deviceId, toAdd, toRemove); 714 log.trace("update backup {} +{} -{}", deviceId, toAdd, toRemove);
708 final SMap<FlowId, ImmutableList<StoredFlowEntry>> backupFlowTable = smaps.get(deviceId); 715 final SMap<FlowId, ImmutableList<StoredFlowEntry>> backupFlowTable = smaps.get(deviceId);
709 // Following should be rewritten using async APIs 716 // Following should be rewritten using async APIs
710 - for (StoredFlowEntry entry : toAdd) { 717 + for (FlowRuleBatchEntry bEntry : toAdd) {
718 + final FlowRule entry = bEntry.getTarget();
711 final FlowId id = entry.id(); 719 final FlowId id = entry.id();
712 ImmutableList<StoredFlowEntry> original = backupFlowTable.get(id); 720 ImmutableList<StoredFlowEntry> original = backupFlowTable.get(id);
713 List<StoredFlowEntry> list = new ArrayList<>(); 721 List<StoredFlowEntry> list = new ArrayList<>();
...@@ -715,8 +723,8 @@ public class DistributedFlowRuleStore ...@@ -715,8 +723,8 @@ public class DistributedFlowRuleStore
715 list.addAll(original); 723 list.addAll(original);
716 } 724 }
717 725
718 - list.remove(entry); 726 + list.remove(bEntry.getTarget());
719 - list.add(entry); 727 + list.add((StoredFlowEntry) entry);
720 728
721 ImmutableList<StoredFlowEntry> newValue = ImmutableList.copyOf(list); 729 ImmutableList<StoredFlowEntry> newValue = ImmutableList.copyOf(list);
722 boolean success; 730 boolean success;
...@@ -730,7 +738,8 @@ public class DistributedFlowRuleStore ...@@ -730,7 +738,8 @@ public class DistributedFlowRuleStore
730 log.error("Updating backup failed."); 738 log.error("Updating backup failed.");
731 } 739 }
732 } 740 }
733 - for (FlowRule entry : toRemove) { 741 + for (FlowRuleBatchEntry bEntry : toRemove) {
742 + final FlowRule entry = bEntry.getTarget();
734 final FlowId id = entry.id(); 743 final FlowId id = entry.id();
735 ImmutableList<StoredFlowEntry> original = backupFlowTable.get(id); 744 ImmutableList<StoredFlowEntry> original = backupFlowTable.get(id);
736 List<StoredFlowEntry> list = new ArrayList<>(); 745 List<StoredFlowEntry> list = new ArrayList<>();
...@@ -738,7 +747,7 @@ public class DistributedFlowRuleStore ...@@ -738,7 +747,7 @@ public class DistributedFlowRuleStore
738 list.addAll(original); 747 list.addAll(original);
739 } 748 }
740 749
741 - list.remove(entry); 750 + list.remove(bEntry.getTarget());
742 751
743 ImmutableList<StoredFlowEntry> newValue = ImmutableList.copyOf(list); 752 ImmutableList<StoredFlowEntry> newValue = ImmutableList.copyOf(list);
744 boolean success; 753 boolean success;
......
...@@ -25,7 +25,6 @@ import org.onlab.onos.net.intent.IntentBatchService; ...@@ -25,7 +25,6 @@ import org.onlab.onos.net.intent.IntentBatchService;
25 import org.onlab.onos.net.intent.IntentOperations; 25 import org.onlab.onos.net.intent.IntentOperations;
26 import org.slf4j.Logger; 26 import org.slf4j.Logger;
27 27
28 -import java.util.Collection;
29 import java.util.LinkedList; 28 import java.util.LinkedList;
30 import java.util.Queue; 29 import java.util.Queue;
31 import java.util.Set; 30 import java.util.Set;
...@@ -82,10 +81,17 @@ public class DistributedIntentBatchQueue implements IntentBatchService { ...@@ -82,10 +81,17 @@ public class DistributedIntentBatchQueue implements IntentBatchService {
82 } 81 }
83 82
84 @Override 83 @Override
85 - public Set<IntentOperations> getIntentOperations() { 84 + public Set<IntentOperations> getPendingOperations() {
86 - Set<IntentOperations> set = Sets.newHashSet(currentBatches); 85 + synchronized (this) {
87 - set.addAll((Collection) pendingBatches); 86 + return Sets.newHashSet(pendingBatches);
88 - return set; 87 + }
88 + }
89 +
90 + @Override
91 + public Set<IntentOperations> getCurrentOperations() {
92 + synchronized (this) {
93 + return Sets.newHashSet(currentBatches);
94 + }
89 } 95 }
90 96
91 @Override 97 @Override
......
...@@ -263,19 +263,19 @@ public class SimpleFlowRuleStore ...@@ -263,19 +263,19 @@ public class SimpleFlowRuleStore
263 @Override 263 @Override
264 public Future<CompletedBatchOperation> storeBatch( 264 public Future<CompletedBatchOperation> storeBatch(
265 FlowRuleBatchOperation batchOperation) { 265 FlowRuleBatchOperation batchOperation) {
266 - List<FlowRule> toAdd = new ArrayList<>(); 266 + List<FlowRuleBatchEntry> toAdd = new ArrayList<>();
267 - List<FlowRule> toRemove = new ArrayList<>(); 267 + List<FlowRuleBatchEntry> toRemove = new ArrayList<>();
268 for (FlowRuleBatchEntry entry : batchOperation.getOperations()) { 268 for (FlowRuleBatchEntry entry : batchOperation.getOperations()) {
269 final FlowRule flowRule = entry.getTarget(); 269 final FlowRule flowRule = entry.getTarget();
270 if (entry.getOperator().equals(FlowRuleOperation.ADD)) { 270 if (entry.getOperator().equals(FlowRuleOperation.ADD)) {
271 if (!getFlowEntries(flowRule.deviceId(), flowRule.id()).contains(flowRule)) { 271 if (!getFlowEntries(flowRule.deviceId(), flowRule.id()).contains(flowRule)) {
272 storeFlowRule(flowRule); 272 storeFlowRule(flowRule);
273 - toAdd.add(flowRule); 273 + toAdd.add(entry);
274 } 274 }
275 } else if (entry.getOperator().equals(FlowRuleOperation.REMOVE)) { 275 } else if (entry.getOperator().equals(FlowRuleOperation.REMOVE)) {
276 if (getFlowEntries(flowRule.deviceId(), flowRule.id()).contains(flowRule)) { 276 if (getFlowEntries(flowRule.deviceId(), flowRule.id()).contains(flowRule)) {
277 deleteFlowRule(flowRule); 277 deleteFlowRule(flowRule);
278 - toRemove.add(flowRule); 278 + toRemove.add(entry);
279 } 279 }
280 } else { 280 } else {
281 throw new UnsupportedOperationException("Unsupported operation type"); 281 throw new UnsupportedOperationException("Unsupported operation type");
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
15 */ 15 */
16 package org.onlab.onos.store.trivial.impl; 16 package org.onlab.onos.store.trivial.impl;
17 17
18 -import com.google.common.collect.ImmutableSet; 18 +import com.google.common.collect.Sets;
19 import org.apache.felix.scr.annotations.Activate; 19 import org.apache.felix.scr.annotations.Activate;
20 import org.apache.felix.scr.annotations.Component; 20 import org.apache.felix.scr.annotations.Component;
21 import org.apache.felix.scr.annotations.Deactivate; 21 import org.apache.felix.scr.annotations.Deactivate;
...@@ -25,7 +25,8 @@ import org.onlab.onos.net.intent.IntentBatchService; ...@@ -25,7 +25,8 @@ import org.onlab.onos.net.intent.IntentBatchService;
25 import org.onlab.onos.net.intent.IntentOperations; 25 import org.onlab.onos.net.intent.IntentOperations;
26 import org.slf4j.Logger; 26 import org.slf4j.Logger;
27 27
28 -import java.util.HashSet; 28 +import java.util.LinkedList;
29 +import java.util.Queue;
29 import java.util.Set; 30 import java.util.Set;
30 31
31 import static com.google.common.base.Preconditions.checkNotNull; 32 import static com.google.common.base.Preconditions.checkNotNull;
...@@ -37,7 +38,8 @@ import static org.slf4j.LoggerFactory.getLogger; ...@@ -37,7 +38,8 @@ import static org.slf4j.LoggerFactory.getLogger;
37 public class SimpleIntentBatchQueue implements IntentBatchService { 38 public class SimpleIntentBatchQueue implements IntentBatchService {
38 39
39 private final Logger log = getLogger(getClass()); 40 private final Logger log = getLogger(getClass());
40 - private final Set<IntentOperations> pendingBatches = new HashSet<>(); 41 + private final Queue<IntentOperations> pendingBatches = new LinkedList<>();
42 + private final Set<IntentOperations> currentBatches = Sets.newHashSet();
41 private IntentBatchDelegate delegate; 43 private IntentBatchDelegate delegate;
42 44
43 @Activate 45 @Activate
...@@ -53,18 +55,42 @@ public class SimpleIntentBatchQueue implements IntentBatchService { ...@@ -53,18 +55,42 @@ public class SimpleIntentBatchQueue implements IntentBatchService {
53 @Override 55 @Override
54 public void addIntentOperations(IntentOperations operations) { 56 public void addIntentOperations(IntentOperations operations) {
55 checkState(delegate != null, "No delegate set"); 57 checkState(delegate != null, "No delegate set");
56 - pendingBatches.add(operations); 58 + synchronized (this) {
57 - delegate.execute(operations); 59 + pendingBatches.add(operations);
60 + if (currentBatches.isEmpty()) {
61 + IntentOperations work = pendingBatches.poll();
62 + currentBatches.add(work);
63 + delegate.execute(work);
64 + }
65 + }
58 } 66 }
59 67
60 @Override 68 @Override
61 public void removeIntentOperations(IntentOperations operations) { 69 public void removeIntentOperations(IntentOperations operations) {
62 - pendingBatches.remove(operations); 70 + // we allow at most one outstanding batch at a time
71 + synchronized (this) {
72 + checkState(currentBatches.remove(operations), "Operations not found in current ops.");
73 + checkState(currentBatches.isEmpty(), "More than one outstanding batch.");
74 + IntentOperations work = pendingBatches.poll();
75 + if (work != null) {
76 + currentBatches.add(work);
77 + delegate.execute(work);
78 + }
79 + }
63 } 80 }
64 81
65 @Override 82 @Override
66 - public Set<IntentOperations> getIntentOperations() { 83 + public Set<IntentOperations> getPendingOperations() {
67 - return ImmutableSet.copyOf(pendingBatches); 84 + synchronized (this) {
85 + return Sets.newHashSet(pendingBatches);
86 + }
87 + }
88 +
89 + @Override
90 + public Set<IntentOperations> getCurrentOperations() {
91 + synchronized (this) {
92 + return Sets.newHashSet(currentBatches);
93 + }
68 } 94 }
69 95
70 @Override 96 @Override
......
...@@ -53,6 +53,8 @@ import org.projectfloodlight.openflow.types.VlanPcp; ...@@ -53,6 +53,8 @@ import org.projectfloodlight.openflow.types.VlanPcp;
53 import org.projectfloodlight.openflow.types.VlanVid; 53 import org.projectfloodlight.openflow.types.VlanVid;
54 import org.slf4j.Logger; 54 import org.slf4j.Logger;
55 55
56 +import java.util.Optional;
57 +
56 /** 58 /**
57 * Builder for OpenFlow flow mods based on FlowRules. 59 * Builder for OpenFlow flow mods based on FlowRules.
58 */ 60 */
...@@ -63,6 +65,7 @@ public abstract class FlowModBuilder { ...@@ -63,6 +65,7 @@ public abstract class FlowModBuilder {
63 private final OFFactory factory; 65 private final OFFactory factory;
64 private final FlowRule flowRule; 66 private final FlowRule flowRule;
65 private final TrafficSelector selector; 67 private final TrafficSelector selector;
68 + protected final Long xid;
66 69
67 /** 70 /**
68 * Creates a new flow mod builder. 71 * Creates a new flow mod builder.
...@@ -71,12 +74,13 @@ public abstract class FlowModBuilder { ...@@ -71,12 +74,13 @@ public abstract class FlowModBuilder {
71 * @param factory the OpenFlow factory to use to build the flow mod 74 * @param factory the OpenFlow factory to use to build the flow mod
72 * @return the new flow mod builder 75 * @return the new flow mod builder
73 */ 76 */
74 - public static FlowModBuilder builder(FlowRule flowRule, OFFactory factory) { 77 + public static FlowModBuilder builder(FlowRule flowRule,
78 + OFFactory factory, Optional<Long> xid) {
75 switch (factory.getVersion()) { 79 switch (factory.getVersion()) {
76 case OF_10: 80 case OF_10:
77 - return new FlowModBuilderVer10(flowRule, factory); 81 + return new FlowModBuilderVer10(flowRule, factory, xid);
78 case OF_13: 82 case OF_13:
79 - return new FlowModBuilderVer13(flowRule, factory); 83 + return new FlowModBuilderVer13(flowRule, factory, xid);
80 default: 84 default:
81 throw new UnsupportedOperationException( 85 throw new UnsupportedOperationException(
82 "No flow mod builder for protocol version " + factory.getVersion()); 86 "No flow mod builder for protocol version " + factory.getVersion());
...@@ -89,10 +93,12 @@ public abstract class FlowModBuilder { ...@@ -89,10 +93,12 @@ public abstract class FlowModBuilder {
89 * @param flowRule the flow rule to transform into a flow mod 93 * @param flowRule the flow rule to transform into a flow mod
90 * @param factory the OpenFlow factory to use to build the flow mod 94 * @param factory the OpenFlow factory to use to build the flow mod
91 */ 95 */
92 - protected FlowModBuilder(FlowRule flowRule, OFFactory factory) { 96 + protected FlowModBuilder(FlowRule flowRule, OFFactory factory, Optional<Long> xid) {
93 this.factory = factory; 97 this.factory = factory;
94 this.flowRule = flowRule; 98 this.flowRule = flowRule;
95 this.selector = flowRule.selector(); 99 this.selector = flowRule.selector();
100 + this.xid = xid.orElse((long) 0);
101 +
96 } 102 }
97 103
98 /** 104 /**
......
...@@ -18,6 +18,7 @@ package org.onlab.onos.provider.of.flow.impl; ...@@ -18,6 +18,7 @@ package org.onlab.onos.provider.of.flow.impl;
18 import java.util.Collections; 18 import java.util.Collections;
19 import java.util.LinkedList; 19 import java.util.LinkedList;
20 import java.util.List; 20 import java.util.List;
21 +import java.util.Optional;
21 22
22 import org.onlab.onos.net.flow.FlowRule; 23 import org.onlab.onos.net.flow.FlowRule;
23 import org.onlab.onos.net.flow.TrafficTreatment; 24 import org.onlab.onos.net.flow.TrafficTreatment;
...@@ -62,8 +63,9 @@ public class FlowModBuilderVer10 extends FlowModBuilder { ...@@ -62,8 +63,9 @@ public class FlowModBuilderVer10 extends FlowModBuilder {
62 * @param flowRule the flow rule to transform into a flow mod 63 * @param flowRule the flow rule to transform into a flow mod
63 * @param factory the OpenFlow factory to use to build the flow mod 64 * @param factory the OpenFlow factory to use to build the flow mod
64 */ 65 */
65 - protected FlowModBuilderVer10(FlowRule flowRule, OFFactory factory) { 66 + protected FlowModBuilderVer10(FlowRule flowRule,
66 - super(flowRule, factory); 67 + OFFactory factory, Optional<Long> xid) {
68 + super(flowRule, factory, xid);
67 69
68 this.treatment = flowRule.treatment(); 70 this.treatment = flowRule.treatment();
69 } 71 }
...@@ -77,7 +79,7 @@ public class FlowModBuilderVer10 extends FlowModBuilder { ...@@ -77,7 +79,7 @@ public class FlowModBuilderVer10 extends FlowModBuilder {
77 79
78 //TODO: what to do without bufferid? do we assume that there will be a pktout as well? 80 //TODO: what to do without bufferid? do we assume that there will be a pktout as well?
79 OFFlowAdd fm = factory().buildFlowAdd() 81 OFFlowAdd fm = factory().buildFlowAdd()
80 - .setXid(cookie) 82 + .setXid(xid)
81 .setCookie(U64.of(cookie)) 83 .setCookie(U64.of(cookie))
82 .setBufferId(OFBufferId.NO_BUFFER) 84 .setBufferId(OFBufferId.NO_BUFFER)
83 .setActions(actions) 85 .setActions(actions)
...@@ -98,7 +100,7 @@ public class FlowModBuilderVer10 extends FlowModBuilder { ...@@ -98,7 +100,7 @@ public class FlowModBuilderVer10 extends FlowModBuilder {
98 100
99 //TODO: what to do without bufferid? do we assume that there will be a pktout as well? 101 //TODO: what to do without bufferid? do we assume that there will be a pktout as well?
100 OFFlowMod fm = factory().buildFlowModify() 102 OFFlowMod fm = factory().buildFlowModify()
101 - .setXid(cookie) 103 + .setXid(xid)
102 .setCookie(U64.of(cookie)) 104 .setCookie(U64.of(cookie))
103 .setBufferId(OFBufferId.NO_BUFFER) 105 .setBufferId(OFBufferId.NO_BUFFER)
104 .setActions(actions) 106 .setActions(actions)
...@@ -118,7 +120,7 @@ public class FlowModBuilderVer10 extends FlowModBuilder { ...@@ -118,7 +120,7 @@ public class FlowModBuilderVer10 extends FlowModBuilder {
118 long cookie = flowRule().id().value(); 120 long cookie = flowRule().id().value();
119 121
120 OFFlowDelete fm = factory().buildFlowDelete() 122 OFFlowDelete fm = factory().buildFlowDelete()
121 - .setXid(cookie) 123 + .setXid(xid)
122 .setCookie(U64.of(cookie)) 124 .setCookie(U64.of(cookie))
123 .setBufferId(OFBufferId.NO_BUFFER) 125 .setBufferId(OFBufferId.NO_BUFFER)
124 .setActions(actions) 126 .setActions(actions)
......
...@@ -18,6 +18,7 @@ package org.onlab.onos.provider.of.flow.impl; ...@@ -18,6 +18,7 @@ package org.onlab.onos.provider.of.flow.impl;
18 import java.util.Collections; 18 import java.util.Collections;
19 import java.util.LinkedList; 19 import java.util.LinkedList;
20 import java.util.List; 20 import java.util.List;
21 +import java.util.Optional;
21 22
22 import org.onlab.onos.net.flow.FlowRule; 23 import org.onlab.onos.net.flow.FlowRule;
23 import org.onlab.onos.net.flow.TrafficTreatment; 24 import org.onlab.onos.net.flow.TrafficTreatment;
...@@ -70,8 +71,8 @@ public class FlowModBuilderVer13 extends FlowModBuilder { ...@@ -70,8 +71,8 @@ public class FlowModBuilderVer13 extends FlowModBuilder {
70 * @param flowRule the flow rule to transform into a flow mod 71 * @param flowRule the flow rule to transform into a flow mod
71 * @param factory the OpenFlow factory to use to build the flow mod 72 * @param factory the OpenFlow factory to use to build the flow mod
72 */ 73 */
73 - protected FlowModBuilderVer13(FlowRule flowRule, OFFactory factory) { 74 + protected FlowModBuilderVer13(FlowRule flowRule, OFFactory factory, Optional<Long> xid) {
74 - super(flowRule, factory); 75 + super(flowRule, factory, xid);
75 76
76 this.treatment = flowRule.treatment(); 77 this.treatment = flowRule.treatment();
77 } 78 }
...@@ -93,7 +94,7 @@ public class FlowModBuilderVer13 extends FlowModBuilder { ...@@ -93,7 +94,7 @@ public class FlowModBuilderVer13 extends FlowModBuilder {
93 94
94 //TODO: what to do without bufferid? do we assume that there will be a pktout as well? 95 //TODO: what to do without bufferid? do we assume that there will be a pktout as well?
95 OFFlowAdd fm = factory().buildFlowAdd() 96 OFFlowAdd fm = factory().buildFlowAdd()
96 - .setXid(cookie) 97 + .setXid(xid)
97 .setCookie(U64.of(cookie)) 98 .setCookie(U64.of(cookie))
98 .setBufferId(OFBufferId.NO_BUFFER) 99 .setBufferId(OFBufferId.NO_BUFFER)
99 .setActions(actions) 100 .setActions(actions)
...@@ -117,7 +118,7 @@ public class FlowModBuilderVer13 extends FlowModBuilder { ...@@ -117,7 +118,7 @@ public class FlowModBuilderVer13 extends FlowModBuilder {
117 118
118 //TODO: what to do without bufferid? do we assume that there will be a pktout as well? 119 //TODO: what to do without bufferid? do we assume that there will be a pktout as well?
119 OFFlowMod fm = factory().buildFlowModify() 120 OFFlowMod fm = factory().buildFlowModify()
120 - .setXid(cookie) 121 + .setXid(xid)
121 .setCookie(U64.of(cookie)) 122 .setCookie(U64.of(cookie))
122 .setBufferId(OFBufferId.NO_BUFFER) 123 .setBufferId(OFBufferId.NO_BUFFER)
123 .setActions(actions) 124 .setActions(actions)
...@@ -140,7 +141,7 @@ public class FlowModBuilderVer13 extends FlowModBuilder { ...@@ -140,7 +141,7 @@ public class FlowModBuilderVer13 extends FlowModBuilder {
140 long cookie = flowRule().id().value(); 141 long cookie = flowRule().id().value();
141 142
142 OFFlowDelete fm = factory().buildFlowDelete() 143 OFFlowDelete fm = factory().buildFlowDelete()
143 - .setXid(cookie) 144 + .setXid(xid)
144 .setCookie(U64.of(cookie)) 145 .setCookie(U64.of(cookie))
145 .setBufferId(OFBufferId.NO_BUFFER) 146 .setBufferId(OFBufferId.NO_BUFFER)
146 //.setActions(actions) //FIXME do we want to send actions in flowdel? 147 //.setActions(actions) //FIXME do we want to send actions in flowdel?
......
...@@ -19,7 +19,6 @@ import com.google.common.collect.ArrayListMultimap; ...@@ -19,7 +19,6 @@ import com.google.common.collect.ArrayListMultimap;
19 import com.google.common.collect.Maps; 19 import com.google.common.collect.Maps;
20 import com.google.common.collect.Multimap; 20 import com.google.common.collect.Multimap;
21 import com.google.common.collect.Sets; 21 import com.google.common.collect.Sets;
22 -import com.google.common.util.concurrent.ExecutionList;
23 import org.apache.felix.scr.annotations.Activate; 22 import org.apache.felix.scr.annotations.Activate;
24 import org.apache.felix.scr.annotations.Component; 23 import org.apache.felix.scr.annotations.Component;
25 import org.apache.felix.scr.annotations.Deactivate; 24 import org.apache.felix.scr.annotations.Deactivate;
...@@ -76,6 +75,7 @@ import java.util.Collections; ...@@ -76,6 +75,7 @@ import java.util.Collections;
76 import java.util.HashMap; 75 import java.util.HashMap;
77 import java.util.List; 76 import java.util.List;
78 import java.util.Map; 77 import java.util.Map;
78 +import java.util.Optional;
79 import java.util.Set; 79 import java.util.Set;
80 import java.util.concurrent.ConcurrentHashMap; 80 import java.util.concurrent.ConcurrentHashMap;
81 import java.util.concurrent.CountDownLatch; 81 import java.util.concurrent.CountDownLatch;
...@@ -122,7 +122,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -122,7 +122,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
122 122
123 private final Map<Dpid, FlowStatsCollector> collectors = Maps.newHashMap(); 123 private final Map<Dpid, FlowStatsCollector> collectors = Maps.newHashMap();
124 124
125 - private final AtomicLong xidCounter = new AtomicLong(0); 125 + private final AtomicLong xidCounter = new AtomicLong(1);
126 126
127 /** 127 /**
128 * Creates an OpenFlow host provider. 128 * Creates an OpenFlow host provider.
...@@ -164,7 +164,8 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -164,7 +164,8 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
164 164
165 private void applyRule(FlowRule flowRule) { 165 private void applyRule(FlowRule flowRule) {
166 OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId().uri())); 166 OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId().uri()));
167 - sw.sendMsg(FlowModBuilder.builder(flowRule, sw.factory()).buildFlowAdd()); 167 + sw.sendMsg(FlowModBuilder.builder(flowRule, sw.factory(),
168 + Optional.empty()).buildFlowAdd());
168 } 169 }
169 170
170 171
...@@ -178,7 +179,8 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -178,7 +179,8 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
178 179
179 private void removeRule(FlowRule flowRule) { 180 private void removeRule(FlowRule flowRule) {
180 OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId().uri())); 181 OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId().uri()));
181 - sw.sendMsg(FlowModBuilder.builder(flowRule, sw.factory()).buildFlowDel()); 182 + sw.sendMsg(FlowModBuilder.builder(flowRule, sw.factory(),
183 + Optional.empty()).buildFlowDel());
182 } 184 }
183 185
184 @Override 186 @Override
...@@ -211,7 +213,10 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -211,7 +213,10 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
211 return failed; 213 return failed;
212 } 214 }
213 sws.add(new Dpid(sw.getId())); 215 sws.add(new Dpid(sw.getId()));
214 - FlowModBuilder builder = FlowModBuilder.builder(flowRule, sw.factory()); 216 + Long flowModXid = xidCounter.getAndIncrement();
217 + FlowModBuilder builder =
218 + FlowModBuilder.builder(flowRule, sw.factory(),
219 + Optional.of(flowModXid));
215 OFFlowMod mod = null; 220 OFFlowMod mod = null;
216 switch (fbe.getOperator()) { 221 switch (fbe.getOperator()) {
217 case ADD: 222 case ADD:
...@@ -228,7 +233,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -228,7 +233,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
228 } 233 }
229 if (mod != null) { 234 if (mod != null) {
230 mods.put(mod, sw); 235 mods.put(mod, sw);
231 - fmXids.put(xidCounter.getAndIncrement(), fbe); 236 + fmXids.put(flowModXid, fbe);
232 } else { 237 } else {
233 log.error("Conversion of flowrule {} failed.", flowRule); 238 log.error("Conversion of flowrule {} failed.", flowRule);
234 } 239 }
...@@ -237,6 +242,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -237,6 +242,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
237 for (Long xid : fmXids.keySet()) { 242 for (Long xid : fmXids.keySet()) {
238 pendingFMs.put(xid, installation); 243 pendingFMs.put(xid, installation);
239 } 244 }
245 +
240 pendingFutures.put(installation.xid(), installation); 246 pendingFutures.put(installation.xid(), installation);
241 for (Map.Entry<OFFlowMod, OpenFlowSwitch> entry : mods.entrySet()) { 247 for (Map.Entry<OFFlowMod, OpenFlowSwitch> entry : mods.entrySet()) {
242 OpenFlowSwitch sw = entry.getValue(); 248 OpenFlowSwitch sw = entry.getValue();
...@@ -368,13 +374,13 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -368,13 +374,13 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
368 private final AtomicBoolean ok = new AtomicBoolean(true); 374 private final AtomicBoolean ok = new AtomicBoolean(true);
369 private final Map<Long, FlowRuleBatchEntry> fms; 375 private final Map<Long, FlowRuleBatchEntry> fms;
370 376
377 +
371 private final Set<FlowEntry> offendingFlowMods = Sets.newHashSet(); 378 private final Set<FlowEntry> offendingFlowMods = Sets.newHashSet();
379 + private Long failedId;
372 380
373 private final CountDownLatch countDownLatch; 381 private final CountDownLatch countDownLatch;
374 private BatchState state; 382 private BatchState state;
375 383
376 - private final ExecutionList executionList = new ExecutionList();
377 -
378 public InstallationFuture(Set<Dpid> sws, Map<Long, FlowRuleBatchEntry> fmXids) { 384 public InstallationFuture(Set<Dpid> sws, Map<Long, FlowRuleBatchEntry> fmXids) {
379 this.xid = xidCounter.getAndIncrement(); 385 this.xid = xidCounter.getAndIncrement();
380 this.state = BatchState.STARTED; 386 this.state = BatchState.STARTED;
...@@ -393,6 +399,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -393,6 +399,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
393 removeRequirement(dpid); 399 removeRequirement(dpid);
394 FlowEntry fe = null; 400 FlowEntry fe = null;
395 FlowRuleBatchEntry fbe = fms.get(msg.getXid()); 401 FlowRuleBatchEntry fbe = fms.get(msg.getXid());
402 + failedId = fbe.id();
396 FlowRule offending = fbe.getTarget(); 403 FlowRule offending = fbe.getTarget();
397 //TODO handle specific error msgs 404 //TODO handle specific error msgs
398 switch (msg.getErrType()) { 405 switch (msg.getErrType()) {
...@@ -492,8 +499,11 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -492,8 +499,11 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
492 public CompletedBatchOperation get() throws InterruptedException, ExecutionException { 499 public CompletedBatchOperation get() throws InterruptedException, ExecutionException {
493 countDownLatch.await(); 500 countDownLatch.await();
494 this.state = BatchState.FINISHED; 501 this.state = BatchState.FINISHED;
495 - CompletedBatchOperation result = new CompletedBatchOperation(ok.get(), offendingFlowMods); 502 + Set<Long> failedIds = (failedId != null) ? Sets.newHashSet(failedId) : Collections.emptySet();
496 - //FIXME do cleanup here 503 + CompletedBatchOperation result =
504 + new CompletedBatchOperation(ok.get(), offendingFlowMods, failedIds);
505 + //FIXME do cleanup here (moved by BOC)
506 + cleanUp();
497 return result; 507 return result;
498 } 508 }
499 509
...@@ -503,8 +513,11 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -503,8 +513,11 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
503 TimeoutException { 513 TimeoutException {
504 if (countDownLatch.await(timeout, unit)) { 514 if (countDownLatch.await(timeout, unit)) {
505 this.state = BatchState.FINISHED; 515 this.state = BatchState.FINISHED;
506 - CompletedBatchOperation result = new CompletedBatchOperation(ok.get(), offendingFlowMods); 516 + Set<Long> failedIds = (failedId != null) ? Sets.newHashSet(failedId) : Collections.emptySet();
507 - // FIXME do cleanup here 517 + CompletedBatchOperation result =
518 + new CompletedBatchOperation(ok.get(), offendingFlowMods, failedIds);
519 + // FIXME do cleanup here (moved by BOC)
520 + cleanUp();
508 return result; 521 return result;
509 } 522 }
510 throw new TimeoutException(); 523 throw new TimeoutException();
...@@ -522,8 +535,8 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -522,8 +535,8 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
522 private void removeRequirement(Dpid dpid) { 535 private void removeRequirement(Dpid dpid) {
523 countDownLatch.countDown(); 536 countDownLatch.countDown();
524 sws.remove(dpid); 537 sws.remove(dpid);
525 - //FIXME don't do cleanup here 538 + //FIXME don't do cleanup here (moved by BOC)
526 - cleanUp(); 539 + //cleanUp();
527 } 540 }
528 } 541 }
529 542
......