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? | ... | ... |
providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/OpenFlowRuleProvider.java
... | @@ -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 | ... | ... |
-
Please register or login to post a comment