Brian O'Connor
Committed by Gerrit Code Review

Initial implementation of distributed intent batch queue

Change-Id: I7ffed03651569ade1be1e8dca905bfaf369b7e03
...@@ -308,7 +308,7 @@ public class DemoInstaller implements DemoAPI { ...@@ -308,7 +308,7 @@ public class DemoInstaller implements DemoAPI {
308 } 308 }
309 309
310 private void installIntents(List<HostPair> toInstall) { 310 private void installIntents(List<HostPair> toInstall) {
311 - IntentOperations.Builder builder = IntentOperations.builder(); 311 + IntentOperations.Builder builder = IntentOperations.builder(appId);
312 for (HostPair pair : toInstall) { 312 for (HostPair pair : toInstall) {
313 installed.add(pair); 313 installed.add(pair);
314 uninstalledOrWithdrawn.remove(pair); 314 uninstalledOrWithdrawn.remove(pair);
...@@ -318,7 +318,7 @@ public class DemoInstaller implements DemoAPI { ...@@ -318,7 +318,7 @@ public class DemoInstaller implements DemoAPI {
318 } 318 }
319 319
320 private void uninstallIntents(Collection<HostPair> toRemove) { 320 private void uninstallIntents(Collection<HostPair> toRemove) {
321 - IntentOperations.Builder builder = IntentOperations.builder(); 321 + IntentOperations.Builder builder = IntentOperations.builder(appId);
322 for (HostPair pair : toRemove) { 322 for (HostPair pair : toRemove) {
323 installed.remove(pair); 323 installed.remove(pair);
324 uninstalledOrWithdrawn.add(pair); 324 uninstalledOrWithdrawn.add(pair);
...@@ -333,7 +333,7 @@ public class DemoInstaller implements DemoAPI { ...@@ -333,7 +333,7 @@ public class DemoInstaller implements DemoAPI {
333 private void cleanUp() { 333 private void cleanUp() {
334 List<HostPair> allPairs = Lists.newArrayList(installed); 334 List<HostPair> allPairs = Lists.newArrayList(installed);
335 allPairs.addAll(uninstalledOrWithdrawn); 335 allPairs.addAll(uninstalledOrWithdrawn);
336 - IntentOperations.Builder builder = IntentOperations.builder(); 336 + IntentOperations.Builder builder = IntentOperations.builder(appId);
337 for (HostPair pair : allPairs) { 337 for (HostPair pair : allPairs) {
338 builder.addWithdrawOperation(pair.h2hIntent().id()); 338 builder.addWithdrawOperation(pair.h2hIntent().id());
339 } 339 }
......
...@@ -129,7 +129,7 @@ public class OpticalPathProvisioner { ...@@ -129,7 +129,7 @@ public class OpticalPathProvisioner {
129 } 129 }
130 130
131 // Build the intent batch 131 // Build the intent batch
132 - IntentOperations.Builder ops = IntentOperations.builder(); 132 + IntentOperations.Builder ops = IntentOperations.builder(appId);
133 for (Intent i : intents) { 133 for (Intent i : intents) {
134 // TODO: don't allow duplicate intents between the same points for now 134 // TODO: don't allow duplicate intents between the same points for now
135 // we may want to allow this carefully in future to increase capacity 135 // we may want to allow this carefully in future to increase capacity
......
...@@ -221,7 +221,7 @@ public class IntentSynchronizer { ...@@ -221,7 +221,7 @@ public class IntentSynchronizer {
221 // Push the intents 221 // Push the intents
222 if (isElectedLeader && isActivatedLeader) { 222 if (isElectedLeader && isActivatedLeader) {
223 log.debug("SDN-IP Submitting all Peer Intents..."); 223 log.debug("SDN-IP Submitting all Peer Intents...");
224 - IntentOperations.Builder builder = IntentOperations.builder(); 224 + IntentOperations.Builder builder = IntentOperations.builder(appId);
225 for (Intent intent : intents) { 225 for (Intent intent : intents) {
226 builder.addSubmitOperation(intent); 226 builder.addSubmitOperation(intent);
227 } 227 }
...@@ -370,7 +370,7 @@ public class IntentSynchronizer { ...@@ -370,7 +370,7 @@ public class IntentSynchronizer {
370 } 370 }
371 371
372 // Withdraw Intents 372 // Withdraw Intents
373 - IntentOperations.Builder builder = IntentOperations.builder(); 373 + IntentOperations.Builder builder = IntentOperations.builder(appId);
374 for (Intent intent : deleteIntents) { 374 for (Intent intent : deleteIntents) {
375 builder.addWithdrawOperation(intent.id()); 375 builder.addWithdrawOperation(intent.id());
376 log.debug("SDN-IP Intent Synchronizer: withdrawing intent: {}", 376 log.debug("SDN-IP Intent Synchronizer: withdrawing intent: {}",
...@@ -386,7 +386,7 @@ public class IntentSynchronizer { ...@@ -386,7 +386,7 @@ public class IntentSynchronizer {
386 intentService.execute(intentOperations); 386 intentService.execute(intentOperations);
387 387
388 // Add Intents 388 // Add Intents
389 - builder = IntentOperations.builder(); 389 + builder = IntentOperations.builder(appId);
390 for (Intent intent : addIntents) { 390 for (Intent intent : addIntents) {
391 builder.addSubmitOperation(intent); 391 builder.addSubmitOperation(intent);
392 log.debug("SDN-IP Intent Synchronizer: submitting intent: {}", 392 log.debug("SDN-IP Intent Synchronizer: submitting intent: {}",
......
...@@ -325,13 +325,13 @@ public class IntentSyncTest extends AbstractIntentTest { ...@@ -325,13 +325,13 @@ public class IntentSyncTest extends AbstractIntentTest {
325 .andReturn(IntentState.WITHDRAWING).anyTimes(); 325 .andReturn(IntentState.WITHDRAWING).anyTimes();
326 expect(intentService.getIntents()).andReturn(intents).anyTimes(); 326 expect(intentService.getIntents()).andReturn(intents).anyTimes();
327 327
328 - IntentOperations.Builder builder = IntentOperations.builder(); 328 + IntentOperations.Builder builder = IntentOperations.builder(null); //FIXME null
329 builder.addWithdrawOperation(intent2.id()); 329 builder.addWithdrawOperation(intent2.id());
330 builder.addWithdrawOperation(intent4.id()); 330 builder.addWithdrawOperation(intent4.id());
331 intentService.execute(TestIntentServiceHelper.eqExceptId( 331 intentService.execute(TestIntentServiceHelper.eqExceptId(
332 builder.build())); 332 builder.build()));
333 333
334 - builder = IntentOperations.builder(); 334 + builder = IntentOperations.builder(null); //FIXME null
335 builder.addSubmitOperation(intent3); 335 builder.addSubmitOperation(intent3);
336 builder.addSubmitOperation(intent4Update); 336 builder.addSubmitOperation(intent4Update);
337 builder.addSubmitOperation(intent6); 337 builder.addSubmitOperation(intent6);
......
...@@ -566,7 +566,7 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest { ...@@ -566,7 +566,7 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
566 reset(intentService); 566 reset(intentService);
567 567
568 // Setup the expected intents 568 // Setup the expected intents
569 - IntentOperations.Builder builder = IntentOperations.builder(); 569 + IntentOperations.Builder builder = IntentOperations.builder(null); //FIXME null
570 for (Intent intent : intentList) { 570 for (Intent intent : intentList) {
571 builder.addSubmitOperation(intent); 571 builder.addSubmitOperation(intent);
572 } 572 }
...@@ -601,9 +601,9 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest { ...@@ -601,9 +601,9 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
601 replay(configInfoService); 601 replay(configInfoService);
602 602
603 reset(intentService); 603 reset(intentService);
604 - IntentOperations.Builder builder = IntentOperations.builder(); 604 + IntentOperations.Builder builder = IntentOperations.builder(null); //FIXME null
605 intentService.execute(TestIntentServiceHelper.eqExceptId( 605 intentService.execute(TestIntentServiceHelper.eqExceptId(
606 - builder.build())); 606 + builder.build()));
607 replay(intentService); 607 replay(intentService);
608 peerConnectivityManager.start(); 608 peerConnectivityManager.start();
609 verify(intentService); 609 verify(intentService);
...@@ -627,9 +627,9 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest { ...@@ -627,9 +627,9 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
627 replay(configInfoService); 627 replay(configInfoService);
628 628
629 reset(intentService); 629 reset(intentService);
630 - IntentOperations.Builder builder = IntentOperations.builder(); 630 + IntentOperations.Builder builder = IntentOperations.builder(null); //FIXME null
631 intentService.execute(TestIntentServiceHelper.eqExceptId( 631 intentService.execute(TestIntentServiceHelper.eqExceptId(
632 - builder.build())); 632 + builder.build()));
633 replay(intentService); 633 replay(intentService);
634 peerConnectivityManager.start(); 634 peerConnectivityManager.start();
635 verify(intentService); 635 verify(intentService);
......
...@@ -119,7 +119,7 @@ public class IntentPushTestCommand extends AbstractShellCommand ...@@ -119,7 +119,7 @@ public class IntentPushTestCommand extends AbstractShellCommand
119 } 119 }
120 120
121 private void submitIntents(List<Intent> intents) { 121 private void submitIntents(List<Intent> intents) {
122 - IntentOperations.Builder builder = IntentOperations.builder(); 122 + IntentOperations.Builder builder = IntentOperations.builder(appId());
123 for (Intent intent : intents) { 123 for (Intent intent : intents) {
124 if (add) { 124 if (add) {
125 builder.addSubmitOperation(intent); 125 builder.addSubmitOperation(intent);
...@@ -132,7 +132,7 @@ public class IntentPushTestCommand extends AbstractShellCommand ...@@ -132,7 +132,7 @@ public class IntentPushTestCommand extends AbstractShellCommand
132 start = System.currentTimeMillis(); 132 start = System.currentTimeMillis();
133 service.execute(ops); 133 service.execute(ops);
134 try { 134 try {
135 - if (latch.await(10, TimeUnit.SECONDS)) { 135 + if (latch.await(30, TimeUnit.SECONDS)) {
136 printResults(count); 136 printResults(count);
137 } else { 137 } else {
138 print("Failure: %d intents not installed", latch.getCount()); 138 print("Failure: %d intents not installed", latch.getCount());
......
...@@ -86,7 +86,7 @@ public class RandomIntentCommand extends AbstractShellCommand { ...@@ -86,7 +86,7 @@ public class RandomIntentCommand extends AbstractShellCommand {
86 } 86 }
87 87
88 private void submitIntents(Collection<Intent> intents) { 88 private void submitIntents(Collection<Intent> intents) {
89 - IntentOperations.Builder builder = IntentOperations.builder(); 89 + IntentOperations.Builder builder = IntentOperations.builder(appId());
90 for (Intent intent : intents) { 90 for (Intent intent : intents) {
91 builder.addSubmitOperation(intent); 91 builder.addSubmitOperation(intent);
92 } 92 }
...@@ -95,7 +95,7 @@ public class RandomIntentCommand extends AbstractShellCommand { ...@@ -95,7 +95,7 @@ public class RandomIntentCommand extends AbstractShellCommand {
95 } 95 }
96 96
97 private void withdrawIntents() { 97 private void withdrawIntents() {
98 - IntentOperations.Builder builder = IntentOperations.builder(); 98 + IntentOperations.Builder builder = IntentOperations.builder(appId());
99 for (Intent intent : service.getIntents()) { 99 for (Intent intent : service.getIntents()) {
100 if (appId().equals(intent.appId())) { 100 if (appId().equals(intent.appId())) {
101 builder.addWithdrawOperation(intent.id()); 101 builder.addWithdrawOperation(intent.id());
......
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
15 */ 15 */
16 package org.onlab.onos.net.intent; 16 package org.onlab.onos.net.intent;
17 17
18 +import org.onlab.onos.core.ApplicationId;
19 +
18 import java.util.Set; 20 import java.util.Set;
19 21
20 /** 22 /**
...@@ -46,9 +48,20 @@ public interface IntentBatchService { ...@@ -46,9 +48,20 @@ public interface IntentBatchService {
46 * Returns the set of intent batches currently being processed. 48 * Returns the set of intent batches currently being processed.
47 * @return set of batches 49 * @return set of batches
48 */ 50 */
51 + //TODO we may want to get rid of this method
52 + @Deprecated
49 Set<IntentOperations> getCurrentOperations(); 53 Set<IntentOperations> getCurrentOperations();
50 54
51 /** 55 /**
56 + * Return true if this instance is the local leader for batch
57 + * processing a given application id.
58 + *
59 + * @param applicationId
60 + * @return
61 + */
62 + boolean isLocalLeader(ApplicationId applicationId);
63 +
64 + /**
52 * Sets the batch service delegate. 65 * Sets the batch service delegate.
53 * 66 *
54 * @param delegate delegate to apply 67 * @param delegate delegate to apply
......
...@@ -19,6 +19,7 @@ import java.util.List; ...@@ -19,6 +19,7 @@ import java.util.List;
19 import java.util.Objects; 19 import java.util.Objects;
20 20
21 import com.google.common.collect.ImmutableList; 21 import com.google.common.collect.ImmutableList;
22 +import org.onlab.onos.core.ApplicationId;
22 23
23 import static com.google.common.base.MoreObjects.toStringHelper; 24 import static com.google.common.base.MoreObjects.toStringHelper;
24 import static com.google.common.base.Preconditions.checkNotNull; 25 import static com.google.common.base.Preconditions.checkNotNull;
...@@ -33,14 +34,16 @@ import static org.onlab.onos.net.intent.IntentOperation.Type.WITHDRAW; ...@@ -33,14 +34,16 @@ import static org.onlab.onos.net.intent.IntentOperation.Type.WITHDRAW;
33 public final class IntentOperations { 34 public final class IntentOperations {
34 35
35 private final List<IntentOperation> operations; 36 private final List<IntentOperation> operations;
37 + private final ApplicationId appId;
36 38
37 /** 39 /**
38 * Creates a batch of intent operations using the supplied list. 40 * Creates a batch of intent operations using the supplied list.
39 * 41 *
40 * @param operations list of intent operations 42 * @param operations list of intent operations
41 */ 43 */
42 - private IntentOperations(List<IntentOperation> operations) { 44 + private IntentOperations(List<IntentOperation> operations, ApplicationId appId) {
43 this.operations = operations; 45 this.operations = operations;
46 + this.appId = appId;
44 } 47 }
45 48
46 /** 49 /**
...@@ -52,16 +55,20 @@ public final class IntentOperations { ...@@ -52,16 +55,20 @@ public final class IntentOperations {
52 return operations; 55 return operations;
53 } 56 }
54 57
58 + public ApplicationId appId() {
59 + return appId;
60 + }
61 +
55 /** 62 /**
56 * Returns a builder for intent operation batches. 63 * Returns a builder for intent operation batches.
57 * 64 *
58 * @return intent operations builder 65 * @return intent operations builder
66 + * @param applicationId application id
59 */ 67 */
60 - public static Builder builder() { 68 + public static Builder builder(ApplicationId applicationId) {
61 - return new Builder(); 69 + return new Builder(applicationId);
62 } 70 }
63 71
64 -
65 @Override 72 @Override
66 public int hashCode() { 73 public int hashCode() {
67 return Objects.hash(operations); 74 return Objects.hash(operations);
...@@ -92,9 +99,11 @@ public final class IntentOperations { ...@@ -92,9 +99,11 @@ public final class IntentOperations {
92 public static final class Builder { 99 public static final class Builder {
93 100
94 private final ImmutableList.Builder<IntentOperation> builder = ImmutableList.builder(); 101 private final ImmutableList.Builder<IntentOperation> builder = ImmutableList.builder();
102 + private final ApplicationId appId;
95 103
96 // Public construction is forbidden. 104 // Public construction is forbidden.
97 - private Builder() { 105 + private Builder(ApplicationId appId) {
106 + this.appId = appId;
98 } 107 }
99 108
100 /** 109 /**
...@@ -153,7 +162,7 @@ public final class IntentOperations { ...@@ -153,7 +162,7 @@ public final class IntentOperations {
153 * @return immutable batch of intent operations 162 * @return immutable batch of intent operations
154 */ 163 */
155 public IntentOperations build() { 164 public IntentOperations build() {
156 - return new IntentOperations(builder.build()); 165 + return new IntentOperations(builder.build(), appId);
157 } 166 }
158 167
159 } 168 }
......
...@@ -82,7 +82,7 @@ public interface Lock { ...@@ -82,7 +82,7 @@ public interface Lock {
82 * lock acquisition events 82 * lock acquisition events
83 * @return epoch 83 * @return epoch
84 */ 84 */
85 - long epoch(); 85 + long epoch();
86 86
87 /** 87 /**
88 * Releases the lock. 88 * Releases the lock.
......
...@@ -78,15 +78,15 @@ public class IntentOperationsTest { ...@@ -78,15 +78,15 @@ public class IntentOperationsTest {
78 @Test 78 @Test
79 public void testEquals() { 79 public void testEquals() {
80 final IntentOperations operations1 = 80 final IntentOperations operations1 =
81 - IntentOperations.builder() 81 + IntentOperations.builder(null) //FIXME null
82 .addSubmitOperation(intent) 82 .addSubmitOperation(intent)
83 .build(); 83 .build();
84 final IntentOperations sameAsOperations1 = 84 final IntentOperations sameAsOperations1 =
85 - IntentOperations.builder() 85 + IntentOperations.builder(null) //FIXME null
86 .addSubmitOperation(intent) 86 .addSubmitOperation(intent)
87 .build(); 87 .build();
88 final IntentOperations operations2 = 88 final IntentOperations operations2 =
89 - IntentOperations.builder() 89 + IntentOperations.builder(null) //FIXME null
90 .addReplaceOperation(intent.id(), intent) 90 .addReplaceOperation(intent.id(), intent)
91 .build(); 91 .build();
92 92
...@@ -102,7 +102,7 @@ public class IntentOperationsTest { ...@@ -102,7 +102,7 @@ public class IntentOperationsTest {
102 @Test 102 @Test
103 public void testConstruction() { 103 public void testConstruction() {
104 final IntentOperations operations = 104 final IntentOperations operations =
105 - IntentOperations.builder() 105 + IntentOperations.builder(null) //FIXME
106 .addUpdateOperation(intent.id()) 106 .addUpdateOperation(intent.id())
107 .addWithdrawOperation(intent.id()) 107 .addWithdrawOperation(intent.id())
108 .build(); 108 .build();
......
...@@ -33,6 +33,7 @@ import org.apache.felix.scr.annotations.Deactivate; ...@@ -33,6 +33,7 @@ import org.apache.felix.scr.annotations.Deactivate;
33 import org.apache.felix.scr.annotations.Reference; 33 import org.apache.felix.scr.annotations.Reference;
34 import org.apache.felix.scr.annotations.ReferenceCardinality; 34 import org.apache.felix.scr.annotations.ReferenceCardinality;
35 import org.apache.felix.scr.annotations.Service; 35 import org.apache.felix.scr.annotations.Service;
36 +import org.onlab.onos.core.ApplicationId;
36 import org.onlab.onos.core.CoreService; 37 import org.onlab.onos.core.CoreService;
37 import org.onlab.onos.core.IdGenerator; 38 import org.onlab.onos.core.IdGenerator;
38 import org.onlab.onos.event.AbstractListenerRegistry; 39 import org.onlab.onos.event.AbstractListenerRegistry;
...@@ -151,20 +152,22 @@ public class IntentManager ...@@ -151,20 +152,22 @@ public class IntentManager
151 @Override 152 @Override
152 public void submit(Intent intent) { 153 public void submit(Intent intent) {
153 checkNotNull(intent, INTENT_NULL); 154 checkNotNull(intent, INTENT_NULL);
154 - execute(IntentOperations.builder().addSubmitOperation(intent).build()); 155 + execute(IntentOperations.builder(intent.appId())
156 + .addSubmitOperation(intent).build());
155 } 157 }
156 158
157 @Override 159 @Override
158 public void withdraw(Intent intent) { 160 public void withdraw(Intent intent) {
159 checkNotNull(intent, INTENT_NULL); 161 checkNotNull(intent, INTENT_NULL);
160 - execute(IntentOperations.builder().addWithdrawOperation(intent.id()).build()); 162 + execute(IntentOperations.builder(intent.appId())
163 + .addWithdrawOperation(intent.id()).build());
161 } 164 }
162 165
163 @Override 166 @Override
164 public void replace(IntentId oldIntentId, Intent newIntent) { 167 public void replace(IntentId oldIntentId, Intent newIntent) {
165 checkNotNull(oldIntentId, INTENT_ID_NULL); 168 checkNotNull(oldIntentId, INTENT_ID_NULL);
166 checkNotNull(newIntent, INTENT_NULL); 169 checkNotNull(newIntent, INTENT_NULL);
167 - execute(IntentOperations.builder() 170 + execute(IntentOperations.builder(newIntent.appId())
168 .addReplaceOperation(oldIntentId, newIntent) 171 .addReplaceOperation(oldIntentId, newIntent)
169 .build()); 172 .build());
170 } 173 }
...@@ -489,26 +492,50 @@ public class IntentManager ...@@ -489,26 +492,50 @@ public class IntentManager
489 } 492 }
490 } 493 }
491 494
492 - // Topology change delegate 495 + private void buildAndSubmitBatches(Iterable<IntentId> intentIds,
493 - private class InternalTopoChangeDelegate implements TopologyChangeDelegate { 496 + boolean compileAllFailed) {
494 - @Override 497 + Map<ApplicationId, IntentOperations.Builder> batches = Maps.newHashMap();
495 - public void triggerCompile(Iterable<IntentId> intentIds, 498 + // Attempt recompilation of the specified intents first.
496 - boolean compileAllFailed) { 499 + for (IntentId id : intentIds) {
497 - // Attempt recompilation of the specified intents first. 500 + Intent intent = store.getIntent(id);
498 - IntentOperations.Builder builder = IntentOperations.builder(); 501 + if (intent == null) {
499 - for (IntentId id : intentIds) { 502 + continue;
500 - builder.addUpdateOperation(id);
501 } 503 }
504 + IntentOperations.Builder builder = batches.get(intent.appId());
505 + if (builder == null) {
506 + builder = IntentOperations.builder(intent.appId());
507 + batches.put(intent.appId(), builder);
508 + }
509 + builder.addUpdateOperation(id);
510 + }
502 511
503 - if (compileAllFailed) { 512 + if (compileAllFailed) {
504 - // If required, compile all currently failed intents. 513 + // If required, compile all currently failed intents.
505 - for (Intent intent : getIntents()) { 514 + for (Intent intent : getIntents()) {
506 - if (getIntentState(intent.id()) == FAILED) { 515 + if (getIntentState(intent.id()) == FAILED) {
507 - builder.addUpdateOperation(intent.id()); 516 + IntentOperations.Builder builder = batches.get(intent.appId());
517 + if (builder == null) {
518 + builder = IntentOperations.builder(intent.appId());
519 + batches.put(intent.appId(), builder);
508 } 520 }
521 + builder.addUpdateOperation(intent.id());
509 } 522 }
510 } 523 }
511 - execute(builder.build()); 524 + }
525 +
526 + for (ApplicationId appId : batches.keySet()) {
527 + if (batchService.isLocalLeader(appId)) {
528 + execute(batches.get(appId).build());
529 + }
530 + }
531 + }
532 +
533 + // Topology change delegate
534 + private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
535 + @Override
536 + public void triggerCompile(Iterable<IntentId> intentIds,
537 + boolean compileAllFailed) {
538 + buildAndSubmitBatches(intentIds, compileAllFailed);
512 } 539 }
513 } 540 }
514 541
......
...@@ -216,5 +216,8 @@ public class ObjectiveTracker implements ObjectiveTrackerService { ...@@ -216,5 +216,8 @@ public class ObjectiveTracker implements ObjectiveTrackerService {
216 } 216 }
217 } 217 }
218 218
219 + //TODO consider adding flow rule event tracking
219 220
221 + //FIXME the only intents that will be tracked are events that were
222 + //executed on this instance. Need to have some backup trackers...
220 } 223 }
......
1 +/*
2 + * Copyright 2014 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onlab.onos.store.hz;
17 +
18 +import com.hazelcast.core.IQueue;
19 +import com.hazelcast.core.ItemListener;
20 +import com.hazelcast.monitor.LocalQueueStats;
21 +import org.onlab.onos.store.serializers.StoreSerializer;
22 +
23 +import java.util.Collection;
24 +import java.util.Iterator;
25 +import java.util.concurrent.TimeUnit;
26 +
27 +import static com.google.common.base.Preconditions.checkNotNull;
28 +
29 +// TODO: implementation is incomplete
30 +
31 +/**
32 + * Wrapper around IQueue&lt;byte[]&gt; which serializes/deserializes
33 + * key and value using StoreSerializer.
34 + *
35 + * @param <T> type
36 + */
37 +public class SQueue<T> implements IQueue<T> {
38 +
39 + private final IQueue<byte[]> q;
40 + private final StoreSerializer serializer;
41 +
42 + /**
43 + * Creates a SQueue instance.
44 + *
45 + * @param baseQueue base IQueue to use
46 + * @param serializer serializer to use for both key and value
47 + */
48 + public SQueue(IQueue<byte[]> baseQueue, StoreSerializer serializer) {
49 + this.q = checkNotNull(baseQueue);
50 + this.serializer = checkNotNull(serializer);
51 + }
52 +
53 + private byte[] serialize(Object key) {
54 + return serializer.encode(key);
55 + }
56 +
57 + private T deserialize(byte[] key) {
58 + return serializer.decode(key);
59 + }
60 +
61 + @Override
62 + public boolean add(T t) {
63 + return q.add(serialize(t));
64 + }
65 +
66 + @Override
67 + public boolean offer(T t) {
68 + return q.offer(serialize(t));
69 + }
70 +
71 + @Override
72 + public void put(T t) throws InterruptedException {
73 + q.put(serialize(t));
74 + }
75 +
76 + @Override
77 + public boolean offer(T t, long l, TimeUnit timeUnit) throws InterruptedException {
78 + return q.offer(serialize(t), l, timeUnit);
79 + }
80 +
81 + @Override
82 + public T take() throws InterruptedException {
83 + return deserialize(q.take());
84 + }
85 +
86 + @Override
87 + public T poll(long l, TimeUnit timeUnit) throws InterruptedException {
88 + return deserialize(q.poll(l, timeUnit));
89 + }
90 +
91 + @Override
92 + public int remainingCapacity() {
93 + return q.remainingCapacity();
94 + }
95 +
96 + @Override
97 + public boolean remove(Object o) {
98 + return q.remove(serialize(o));
99 + }
100 +
101 + @Override
102 + public boolean contains(Object o) {
103 + return q.contains(serialize(o));
104 + }
105 +
106 + @Override
107 + public int drainTo(Collection<? super T> collection) {
108 + throw new UnsupportedOperationException();
109 + }
110 +
111 + @Override
112 + public int drainTo(Collection<? super T> collection, int i) {
113 + throw new UnsupportedOperationException();
114 + }
115 +
116 + @Override
117 + public T remove() {
118 + return deserialize(q.remove());
119 + }
120 +
121 + @Override
122 + public T poll() {
123 + return deserialize(q.poll());
124 + }
125 +
126 + @Override
127 + public T element() {
128 + return deserialize(q.element());
129 + }
130 +
131 + @Override
132 + public T peek() {
133 + return deserialize(q.peek());
134 + }
135 +
136 + @Override
137 + public int size() {
138 + return q.size();
139 + }
140 +
141 + @Override
142 + public boolean isEmpty() {
143 + return q.isEmpty();
144 + }
145 +
146 + @Override
147 + public Iterator<T> iterator() {
148 + throw new UnsupportedOperationException();
149 + }
150 +
151 + @Override
152 + public Object[] toArray() {
153 + throw new UnsupportedOperationException();
154 + }
155 +
156 + @Override
157 + public <T1> T1[] toArray(T1[] t1s) {
158 + throw new UnsupportedOperationException();
159 + }
160 +
161 + @Override
162 + public boolean containsAll(Collection<?> collection) {
163 + throw new UnsupportedOperationException();
164 + }
165 +
166 + @Override
167 + public boolean addAll(Collection<? extends T> collection) {
168 + throw new UnsupportedOperationException();
169 + }
170 +
171 + @Override
172 + public boolean removeAll(Collection<?> collection) {
173 + throw new UnsupportedOperationException();
174 + }
175 +
176 + @Override
177 + public boolean retainAll(Collection<?> collection) {
178 + throw new UnsupportedOperationException();
179 + }
180 +
181 + @Override
182 + public void clear() {
183 + q.clear();
184 + }
185 +
186 + @Override
187 + public LocalQueueStats getLocalQueueStats() {
188 + return q.getLocalQueueStats();
189 + }
190 +
191 + @Override
192 + public String addItemListener(ItemListener<T> itemListener, boolean b) {
193 + throw new UnsupportedOperationException();
194 + }
195 +
196 + @Override
197 + public boolean removeItemListener(String s) {
198 + throw new UnsupportedOperationException();
199 + }
200 +
201 + @Deprecated
202 + @Override
203 + public Object getId() {
204 + return q.getId();
205 + }
206 +
207 + @Override
208 + public String getPartitionKey() {
209 + return q.getPartitionKey();
210 + }
211 +
212 + @Override
213 + public String getName() {
214 + return q.getName();
215 + }
216 +
217 + @Override
218 + public String getServiceName() {
219 + return q.getServiceName();
220 + }
221 +
222 + @Override
223 + public void destroy() {
224 + q.destroy();
225 + }
226 +}
1 -/*
2 - * Copyright 2014 Open Networking Laboratory
3 - *
4 - * Licensed under the Apache License, Version 2.0 (the "License");
5 - * you may not use this file except in compliance with the License.
6 - * You may obtain a copy of the License at
7 - *
8 - * http://www.apache.org/licenses/LICENSE-2.0
9 - *
10 - * Unless required by applicable law or agreed to in writing, software
11 - * distributed under the License is distributed on an "AS IS" BASIS,
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 - * See the License for the specific language governing permissions and
14 - * limitations under the License.
15 - */
16 -package org.onlab.onos.store.intent.impl;
17 -
18 -import com.google.common.collect.Sets;
19 -import org.apache.felix.scr.annotations.Activate;
20 -import org.apache.felix.scr.annotations.Component;
21 -import org.apache.felix.scr.annotations.Deactivate;
22 -import org.apache.felix.scr.annotations.Service;
23 -import org.onlab.onos.net.intent.IntentBatchDelegate;
24 -import org.onlab.onos.net.intent.IntentBatchService;
25 -import org.onlab.onos.net.intent.IntentOperations;
26 -import org.slf4j.Logger;
27 -
28 -import java.util.LinkedList;
29 -import java.util.Queue;
30 -import java.util.Set;
31 -
32 -import static com.google.common.base.Preconditions.checkNotNull;
33 -import static com.google.common.base.Preconditions.checkState;
34 -import static org.slf4j.LoggerFactory.getLogger;
35 -
36 -// FIXME This is not distributed yet.
37 -@Component(immediate = true)
38 -@Service
39 -public class DistributedIntentBatchQueue implements IntentBatchService {
40 -
41 - private final Logger log = getLogger(getClass());
42 - private final Queue<IntentOperations> pendingBatches = new LinkedList<>();
43 - private final Set<IntentOperations> currentBatches = Sets.newHashSet();
44 - private IntentBatchDelegate delegate;
45 -
46 - @Activate
47 - public void activate() {
48 - log.info("Started");
49 - }
50 -
51 - @Deactivate
52 - public void deactivate() {
53 - log.info("Stopped");
54 - }
55 -
56 - @Override
57 - public void addIntentOperations(IntentOperations operations) {
58 - checkState(delegate != null, "No delegate set");
59 - synchronized (this) {
60 - pendingBatches.add(operations);
61 - if (currentBatches.isEmpty()) {
62 - IntentOperations work = pendingBatches.poll();
63 - currentBatches.add(work);
64 - delegate.execute(work);
65 - }
66 - }
67 - }
68 -
69 - @Override
70 - public void removeIntentOperations(IntentOperations operations) {
71 - // we allow at most one outstanding batch at a time
72 - synchronized (this) {
73 - checkState(currentBatches.remove(operations), "Operations not found in current ops.");
74 - checkState(currentBatches.isEmpty(), "More than one outstanding batch.");
75 - IntentOperations work = pendingBatches.poll();
76 - if (work != null) {
77 - currentBatches.add(work);
78 - delegate.execute(work);
79 - }
80 - }
81 - }
82 -
83 - @Override
84 - public Set<IntentOperations> getPendingOperations() {
85 - synchronized (this) {
86 - return Sets.newHashSet(pendingBatches);
87 - }
88 - }
89 -
90 - @Override
91 - public Set<IntentOperations> getCurrentOperations() {
92 - synchronized (this) {
93 - return Sets.newHashSet(currentBatches);
94 - }
95 - }
96 -
97 - @Override
98 - public void setDelegate(IntentBatchDelegate delegate) {
99 - this.delegate = checkNotNull(delegate, "Delegate cannot be null");
100 - }
101 -
102 - @Override
103 - public void unsetDelegate(IntentBatchDelegate delegate) {
104 - if (this.delegate != null && this.delegate.equals(delegate)) {
105 - this.delegate = null;
106 - }
107 - }
108 -}
1 +/*
2 + * Copyright 2014 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onlab.onos.store.intent.impl;
17 +
18 +import com.google.common.collect.Maps;
19 +import com.google.common.collect.Sets;
20 +import com.hazelcast.core.HazelcastInstance;
21 +import com.hazelcast.core.IQueue;
22 +import org.apache.felix.scr.annotations.Activate;
23 +import org.apache.felix.scr.annotations.Component;
24 +import org.apache.felix.scr.annotations.Deactivate;
25 +import org.apache.felix.scr.annotations.Reference;
26 +import org.apache.felix.scr.annotations.ReferenceCardinality;
27 +import org.apache.felix.scr.annotations.Service;
28 +import org.onlab.onos.cluster.ClusterService;
29 +import org.onlab.onos.cluster.ControllerNode;
30 +import org.onlab.onos.cluster.LeadershipEvent;
31 +import org.onlab.onos.cluster.LeadershipEventListener;
32 +import org.onlab.onos.cluster.LeadershipService;
33 +import org.onlab.onos.core.ApplicationId;
34 +import org.onlab.onos.core.CoreService;
35 +import org.onlab.onos.net.intent.IntentBatchDelegate;
36 +import org.onlab.onos.net.intent.IntentBatchService;
37 +import org.onlab.onos.net.intent.IntentOperations;
38 +import org.onlab.onos.store.hz.SQueue;
39 +import org.onlab.onos.store.hz.StoreService;
40 +import org.onlab.onos.store.serializers.KryoNamespaces;
41 +import org.onlab.onos.store.serializers.KryoSerializer;
42 +import org.onlab.onos.store.serializers.StoreSerializer;
43 +import org.onlab.util.KryoNamespace;
44 +import org.slf4j.Logger;
45 +
46 +import java.util.Collections;
47 +import java.util.Map;
48 +import java.util.Set;
49 +
50 +import static com.google.common.base.Preconditions.checkNotNull;
51 +import static com.google.common.base.Preconditions.checkState;
52 +import static org.slf4j.LoggerFactory.getLogger;
53 +
54 +@Component(immediate = true)
55 +@Service
56 +public class HazelcastIntentBatchQueue
57 + implements IntentBatchService {
58 +
59 + private final Logger log = getLogger(getClass());
60 + private static final String TOPIC_BASE = "intent-batch-";
61 +
62 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
63 + protected CoreService coreService;
64 +
65 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
66 + protected ClusterService clusterService;
67 +
68 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
69 + protected LeadershipService leadershipService;
70 +
71 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
72 + protected StoreService storeService;
73 +
74 + private HazelcastInstance theInstance;
75 + private ControllerNode localControllerNode;
76 + protected StoreSerializer serializer;
77 + private IntentBatchDelegate delegate;
78 + private InternalLeaderListener leaderListener = new InternalLeaderListener();
79 + private final Map<ApplicationId, SQueue<IntentOperations>> batchQueues
80 + = Maps.newHashMap(); // FIXME make distributed?
81 + private final Set<ApplicationId> myTopics = Sets.newHashSet();
82 + private final Map<ApplicationId, IntentOperations> outstandingOps
83 + = Maps.newHashMap();
84 +
85 + @Activate
86 + public void activate() {
87 + theInstance = storeService.getHazelcastInstance();
88 + localControllerNode = clusterService.getLocalNode();
89 + leadershipService.addListener(leaderListener);
90 +
91 + serializer = new KryoSerializer() {
92 +
93 + @Override
94 + protected void setupKryoPool() {
95 + serializerPool = KryoNamespace.newBuilder()
96 + .setRegistrationRequired(false)
97 + .register(KryoNamespaces.API)
98 + .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
99 + .build();
100 + }
101 +
102 + };
103 + log.info("Started");
104 + }
105 +
106 + @Deactivate
107 + public void deactivate() {
108 + leadershipService.removeListener(leaderListener);
109 + log.info("Stopped");
110 + }
111 +
112 + public static String getTopic(ApplicationId appId) {
113 + return TOPIC_BASE + checkNotNull(appId.id());
114 + }
115 +
116 + public ApplicationId getAppId(String topic) {
117 + checkState(topic.startsWith(TOPIC_BASE),
118 + "Trying to get app id for invalid topic: {}", topic);
119 + Short id = Short.parseShort(topic.substring(TOPIC_BASE.length()));
120 + return coreService.getAppId(id);
121 + }
122 +
123 + private SQueue<IntentOperations> getQueue(ApplicationId appId) {
124 + SQueue<IntentOperations> queue = batchQueues.get(appId);
125 + if (queue == null) {
126 + synchronized (this) {
127 + // FIXME how will other instances find out about new queues
128 + String topic = getTopic(appId);
129 + IQueue<byte[]> rawQueue = theInstance.getQueue(topic);
130 + queue = new SQueue<>(rawQueue, serializer);
131 + batchQueues.putIfAbsent(appId, queue);
132 + // TODO others should run for leadership when they hear about this topic
133 + leadershipService.runForLeadership(topic);
134 + }
135 + }
136 + return queue;
137 + }
138 +
139 + @Override
140 + public void addIntentOperations(IntentOperations ops) {
141 + checkNotNull(ops, "Intent operations cannot be null.");
142 + ApplicationId appId = ops.appId();
143 + getQueue(appId).add(ops); // TODO consider using put here
144 + dispatchNextOperation(appId);
145 + }
146 +
147 + @Override
148 + public void removeIntentOperations(IntentOperations ops) {
149 + ApplicationId appId = ops.appId();
150 + synchronized (this) {
151 + checkState(outstandingOps.get(appId).equals(ops),
152 + "Operations not found.");
153 + SQueue<IntentOperations> queue = batchQueues.get(appId);
154 + // TODO consider alternatives to remove
155 + checkState(queue.remove().equals(ops),
156 + "Operations are wrong.");
157 + outstandingOps.remove(appId);
158 + dispatchNextOperation(appId);
159 + }
160 + }
161 +
162 + /**
163 + * Dispatches the next available operations to the delegate, unless
164 + * we are not the leader for this application id or there is an
165 + * outstanding operations for this application id.
166 + *
167 + * @param appId application id
168 + */
169 + private void dispatchNextOperation(ApplicationId appId) {
170 + synchronized (this) {
171 + if (!myTopics.contains(appId) ||
172 + outstandingOps.containsKey(appId)) {
173 + return;
174 + }
175 + IntentOperations ops = batchQueues.get(appId).peek();
176 + if (ops != null) {
177 + outstandingOps.put(appId, ops);
178 + delegate.execute(ops);
179 + }
180 + }
181 + }
182 +
183 + /**
184 + * Record the leadership change for the given topic. If we have become the
185 + * leader, then dispatch the next operations. If we have lost leadership,
186 + * then cancel the last operations.
187 + *
188 + * @param topic topic based on application id
189 + * @param leader true if we have become the leader, false otherwise
190 + */
191 + private void leaderChanged(String topic, boolean leader) {
192 + ApplicationId appId = getAppId(topic);
193 + //TODO we are using the event caller's thread, should we use our own?
194 + synchronized (this) {
195 + if (leader) {
196 + myTopics.add(appId);
197 + checkState(!outstandingOps.containsKey(appId),
198 + "Existing intent ops for app id: {}", appId);
199 + dispatchNextOperation(appId);
200 + } else {
201 + myTopics.remove(appId);
202 + IntentOperations ops = outstandingOps.get(appId);
203 + if (ops != null) {
204 + delegate.cancel(ops);
205 + }
206 + outstandingOps.remove(appId);
207 + }
208 + }
209 + }
210 +
211 + private class InternalLeaderListener implements LeadershipEventListener {
212 + @Override
213 + public void event(LeadershipEvent event) {
214 + log.debug("Leadership Event: time = {} type = {} event = {}",
215 + event.time(), event.type(), event);
216 +
217 + String topic = event.subject().topic();
218 + if (!topic.startsWith(TOPIC_BASE)) {
219 + return; // Not our topic: ignore
220 + }
221 + if (!event.subject().leader().id().equals(localControllerNode.id())) {
222 + return; // The event is not about this instance: ignore
223 + }
224 +
225 + switch (event.type()) {
226 + case LEADER_ELECTED:
227 + log.info("Elected leader for app {}", getAppId(topic));
228 + leaderChanged(topic, true);
229 + break;
230 + case LEADER_BOOTED:
231 + log.info("Lost leader election for app {}", getAppId(topic));
232 + leaderChanged(topic, false);
233 + break;
234 + case LEADER_REELECTED:
235 + break;
236 + default:
237 + break;
238 + }
239 + }
240 + }
241 +
242 + @Override
243 + public Set<IntentOperations> getPendingOperations() {
244 + Set<IntentOperations> ops = Sets.newHashSet();
245 + synchronized (this) {
246 + for (SQueue<IntentOperations> queue : batchQueues.values()) {
247 + ops.addAll(queue);
248 + }
249 + return ops;
250 + }
251 + }
252 +
253 + @Override
254 + public Set<IntentOperations> getCurrentOperations() {
255 + //FIXME this is not really implemented
256 + return Collections.emptySet();
257 + }
258 +
259 + @Override
260 + public boolean isLocalLeader(ApplicationId applicationId) {
261 + return myTopics.contains(applicationId);
262 + }
263 +
264 + @Override
265 + public void setDelegate(IntentBatchDelegate delegate) {
266 + this.delegate = checkNotNull(delegate, "Delegate cannot be null");
267 + }
268 +
269 + @Override
270 + public void unsetDelegate(IntentBatchDelegate delegate) {
271 + if (this.delegate != null && this.delegate.equals(delegate)) {
272 + this.delegate = null;
273 + }
274 + }
275 +}
...@@ -73,6 +73,8 @@ import org.onlab.onos.net.intent.ConnectivityIntent; ...@@ -73,6 +73,8 @@ import org.onlab.onos.net.intent.ConnectivityIntent;
73 import org.onlab.onos.net.intent.HostToHostIntent; 73 import org.onlab.onos.net.intent.HostToHostIntent;
74 import org.onlab.onos.net.intent.Intent; 74 import org.onlab.onos.net.intent.Intent;
75 import org.onlab.onos.net.intent.IntentId; 75 import org.onlab.onos.net.intent.IntentId;
76 +import org.onlab.onos.net.intent.IntentOperation;
77 +import org.onlab.onos.net.intent.IntentOperations;
76 import org.onlab.onos.net.intent.IntentState; 78 import org.onlab.onos.net.intent.IntentState;
77 import org.onlab.onos.net.intent.LinkCollectionIntent; 79 import org.onlab.onos.net.intent.LinkCollectionIntent;
78 import org.onlab.onos.net.intent.MultiPointToSinglePointIntent; 80 import org.onlab.onos.net.intent.MultiPointToSinglePointIntent;
...@@ -275,7 +277,9 @@ public final class KryoNamespaces { ...@@ -275,7 +277,9 @@ public final class KryoNamespaces {
275 WaypointConstraint.class, 277 WaypointConstraint.class,
276 ObstacleConstraint.class, 278 ObstacleConstraint.class,
277 AnnotationConstraint.class, 279 AnnotationConstraint.class,
278 - BooleanConstraint.class 280 + BooleanConstraint.class,
281 + IntentOperation.class,
282 + IntentOperations.class
279 ) 283 )
280 .register(new DefaultApplicationIdSerializer(), DefaultApplicationId.class) 284 .register(new DefaultApplicationIdSerializer(), DefaultApplicationId.class)
281 .register(new URISerializer(), URI.class) 285 .register(new URISerializer(), URI.class)
......
...@@ -20,6 +20,7 @@ import org.apache.felix.scr.annotations.Activate; ...@@ -20,6 +20,7 @@ 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;
22 import org.apache.felix.scr.annotations.Service; 22 import org.apache.felix.scr.annotations.Service;
23 +import org.onlab.onos.core.ApplicationId;
23 import org.onlab.onos.net.intent.IntentBatchDelegate; 24 import org.onlab.onos.net.intent.IntentBatchDelegate;
24 import org.onlab.onos.net.intent.IntentBatchService; 25 import org.onlab.onos.net.intent.IntentBatchService;
25 import org.onlab.onos.net.intent.IntentOperations; 26 import org.onlab.onos.net.intent.IntentOperations;
...@@ -94,6 +95,11 @@ public class SimpleIntentBatchQueue implements IntentBatchService { ...@@ -94,6 +95,11 @@ public class SimpleIntentBatchQueue implements IntentBatchService {
94 } 95 }
95 96
96 @Override 97 @Override
98 + public boolean isLocalLeader(ApplicationId applicationId) {
99 + return true;
100 + }
101 +
102 + @Override
97 public void setDelegate(IntentBatchDelegate delegate) { 103 public void setDelegate(IntentBatchDelegate delegate) {
98 this.delegate = checkNotNull(delegate, "Delegate cannot be null"); 104 this.delegate = checkNotNull(delegate, "Delegate cannot be null");
99 } 105 }
......