Pavlin Radoslavov

Updates for SDN-IP:

 * Use the new Leadership Service instead of Distributed Lock to
   elect the SDN-IP Leader
 * Reimplement the SDN-IP Intent Synchronizer. In the new implementation
   the Point-to-Point Peer intents are also synchronized by and pushed
   only by the Leader (same as the Multipoint-to-SinglePoint Route intents)
 * Minor cleanups

Change-Id: I8e142781211a1d0f2d362875bc28fd05d843cd4b
...@@ -49,6 +49,12 @@ ...@@ -49,6 +49,12 @@
49 </dependency> 49 </dependency>
50 50
51 <dependency> 51 <dependency>
52 + <groupId>org.apache.commons</groupId>
53 + <artifactId>commons-collections4</artifactId>
54 + <version>4.0</version>
55 + </dependency>
56 +
57 + <dependency>
52 <groupId>org.onlab.onos</groupId> 58 <groupId>org.onlab.onos</groupId>
53 <artifactId>onlab-thirdparty</artifactId> 59 <artifactId>onlab-thirdparty</artifactId>
54 </dependency> 60 </dependency>
...@@ -69,10 +75,12 @@ ...@@ -69,10 +75,12 @@
69 <artifactId>onos-cli</artifactId> 75 <artifactId>onos-cli</artifactId>
70 <version>${project.version}</version> 76 <version>${project.version}</version>
71 </dependency> 77 </dependency>
78 +
72 <dependency> 79 <dependency>
73 <groupId>org.apache.karaf.shell</groupId> 80 <groupId>org.apache.karaf.shell</groupId>
74 <artifactId>org.apache.karaf.shell.console</artifactId> 81 <artifactId>org.apache.karaf.shell.console</artifactId>
75 </dependency> 82 </dependency>
83 +
76 <dependency> 84 <dependency>
77 <groupId>org.osgi</groupId> 85 <groupId>org.osgi</groupId>
78 <artifactId>org.osgi.core</artifactId> 86 <artifactId>org.osgi.core</artifactId>
......
...@@ -20,33 +20,37 @@ import java.util.HashMap; ...@@ -20,33 +20,37 @@ import java.util.HashMap;
20 import java.util.LinkedList; 20 import java.util.LinkedList;
21 import java.util.List; 21 import java.util.List;
22 import java.util.Map; 22 import java.util.Map;
23 +import java.util.Objects;
23 import java.util.concurrent.ConcurrentHashMap; 24 import java.util.concurrent.ConcurrentHashMap;
24 import java.util.concurrent.ExecutorService; 25 import java.util.concurrent.ExecutorService;
25 import java.util.concurrent.Executors; 26 import java.util.concurrent.Executors;
26 import java.util.concurrent.Semaphore; 27 import java.util.concurrent.Semaphore;
27 28
28 -import org.apache.commons.lang3.tuple.Pair;
29 import org.onlab.onos.core.ApplicationId; 29 import org.onlab.onos.core.ApplicationId;
30 import org.onlab.onos.net.flow.criteria.Criteria.IPCriterion; 30 import org.onlab.onos.net.flow.criteria.Criteria.IPCriterion;
31 import org.onlab.onos.net.flow.criteria.Criterion; 31 import org.onlab.onos.net.flow.criteria.Criterion;
32 import org.onlab.onos.net.intent.Intent; 32 import org.onlab.onos.net.intent.Intent;
33 +import org.onlab.onos.net.intent.IntentOperations;
33 import org.onlab.onos.net.intent.IntentService; 34 import org.onlab.onos.net.intent.IntentService;
34 import org.onlab.onos.net.intent.IntentState; 35 import org.onlab.onos.net.intent.IntentState;
35 import org.onlab.onos.net.intent.MultiPointToSinglePointIntent; 36 import org.onlab.onos.net.intent.MultiPointToSinglePointIntent;
37 +import org.onlab.onos.net.intent.PointToPointIntent;
36 import org.onlab.packet.Ip4Prefix; 38 import org.onlab.packet.Ip4Prefix;
37 import org.slf4j.Logger; 39 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory; 40 import org.slf4j.LoggerFactory;
39 41
40 -import com.google.common.base.Objects;
41 import com.google.common.util.concurrent.ThreadFactoryBuilder; 42 import com.google.common.util.concurrent.ThreadFactoryBuilder;
42 43
44 +import static com.google.common.base.Preconditions.checkArgument;
45 +
43 public class IntentSynchronizer { 46 public class IntentSynchronizer {
44 private static final Logger log = 47 private static final Logger log =
45 LoggerFactory.getLogger(IntentSynchronizer.class); 48 LoggerFactory.getLogger(IntentSynchronizer.class);
46 49
47 private final ApplicationId appId; 50 private final ApplicationId appId;
48 private final IntentService intentService; 51 private final IntentService intentService;
49 - private final Map<Ip4Prefix, MultiPointToSinglePointIntent> pushedRouteIntents; 52 + private final Map<IntentKey, PointToPointIntent> peerIntents;
53 + private final Map<Ip4Prefix, MultiPointToSinglePointIntent> routeIntents;
50 54
51 // 55 //
52 // State to deal with SDN-IP Leader election and pushing Intents 56 // State to deal with SDN-IP Leader election and pushing Intents
...@@ -65,7 +69,8 @@ public class IntentSynchronizer { ...@@ -65,7 +69,8 @@ public class IntentSynchronizer {
65 IntentSynchronizer(ApplicationId appId, IntentService intentService) { 69 IntentSynchronizer(ApplicationId appId, IntentService intentService) {
66 this.appId = appId; 70 this.appId = appId;
67 this.intentService = intentService; 71 this.intentService = intentService;
68 - pushedRouteIntents = new ConcurrentHashMap<>(); 72 + peerIntents = new ConcurrentHashMap<>();
73 + routeIntents = new ConcurrentHashMap<>();
69 74
70 bgpIntentsSynchronizerExecutor = Executors.newSingleThreadExecutor( 75 bgpIntentsSynchronizerExecutor = Executors.newSingleThreadExecutor(
71 new ThreadFactoryBuilder() 76 new ThreadFactoryBuilder()
...@@ -88,6 +93,7 @@ public class IntentSynchronizer { ...@@ -88,6 +93,7 @@ public class IntentSynchronizer {
88 * Stops the synchronizer. 93 * Stops the synchronizer.
89 */ 94 */
90 public void stop() { 95 public void stop() {
96 + synchronized (this) {
91 // Stop the thread(s) 97 // Stop the thread(s)
92 bgpIntentsSynchronizerExecutor.shutdownNow(); 98 bgpIntentsSynchronizerExecutor.shutdownNow();
93 99
...@@ -97,19 +103,37 @@ public class IntentSynchronizer { ...@@ -97,19 +103,37 @@ public class IntentSynchronizer {
97 if (!isElectedLeader) { 103 if (!isElectedLeader) {
98 return; // Nothing to do: not the leader anymore 104 return; // Nothing to do: not the leader anymore
99 } 105 }
100 - log.debug("Withdrawing all SDN-IP Route Intents..."); 106 +
107 + //
108 + // Build a batch operation to withdraw all intents from this
109 + // application.
110 + //
111 + log.debug("Withdrawing all SDN-IP Intents...");
112 + IntentOperations.Builder builder = IntentOperations.builder();
101 for (Intent intent : intentService.getIntents()) { 113 for (Intent intent : intentService.getIntents()) {
102 - if (!(intent instanceof MultiPointToSinglePointIntent) 114 + // Skip the intents from other applications
103 - || !intent.appId().equals(appId)) { 115 + if (!intent.appId().equals(appId)) {
104 continue; 116 continue;
105 } 117 }
106 - intentService.withdraw(intent); 118 +
119 + // Skip the intents that are already withdrawn
120 + IntentState intentState =
121 + intentService.getIntentState(intent.id());
122 + if (intentState.equals(IntentState.WITHDRAWING) ||
123 + intentState.equals(IntentState.WITHDRAWN)) {
124 + continue;
125 + }
126 +
127 + builder.addWithdrawOperation(intent.id());
107 } 128 }
129 + intentService.execute(builder.build());
130 + leaderChanged(false);
108 131
109 - pushedRouteIntents.clear(); 132 + peerIntents.clear();
133 + routeIntents.clear();
134 + }
110 } 135 }
111 136
112 - //@Override TODO hook this up to something
113 public void leaderChanged(boolean isLeader) { 137 public void leaderChanged(boolean isLeader) {
114 log.debug("Leader changed: {}", isLeader); 138 log.debug("Leader changed: {}", isLeader);
115 139
...@@ -128,18 +152,18 @@ public class IntentSynchronizer { ...@@ -128,18 +152,18 @@ public class IntentSynchronizer {
128 } 152 }
129 153
130 /** 154 /**
131 - * Gets the pushed route intents. 155 + * Gets the route intents.
132 * 156 *
133 - * @return the pushed route intents 157 + * @return the route intents
134 */ 158 */
135 - public Collection<MultiPointToSinglePointIntent> getPushedRouteIntents() { 159 + public Collection<MultiPointToSinglePointIntent> getRouteIntents() {
136 - List<MultiPointToSinglePointIntent> pushedIntents = new LinkedList<>(); 160 + List<MultiPointToSinglePointIntent> result = new LinkedList<>();
137 161
138 for (Map.Entry<Ip4Prefix, MultiPointToSinglePointIntent> entry : 162 for (Map.Entry<Ip4Prefix, MultiPointToSinglePointIntent> entry :
139 - pushedRouteIntents.entrySet()) { 163 + routeIntents.entrySet()) {
140 - pushedIntents.add(entry.getValue()); 164 + result.add(entry.getValue());
141 } 165 }
142 - return pushedIntents; 166 + return result;
143 } 167 }
144 168
145 /** 169 /**
...@@ -162,7 +186,7 @@ public class IntentSynchronizer { ...@@ -162,7 +186,7 @@ public class IntentSynchronizer {
162 interrupted = true; 186 interrupted = true;
163 break; 187 break;
164 } 188 }
165 - syncIntents(); 189 + synchronizeIntents();
166 } 190 }
167 } finally { 191 } finally {
168 if (interrupted) { 192 if (interrupted) {
...@@ -172,6 +196,29 @@ public class IntentSynchronizer { ...@@ -172,6 +196,29 @@ public class IntentSynchronizer {
172 } 196 }
173 197
174 /** 198 /**
199 + * Submits a collection of point-to-point intents.
200 + *
201 + * @param intents the intents to submit
202 + */
203 + void submitPeerIntents(Collection<PointToPointIntent> intents) {
204 + synchronized (this) {
205 + // Store the intents in memory
206 + for (PointToPointIntent intent : intents) {
207 + peerIntents.put(new IntentKey(intent), intent);
208 + }
209 +
210 + // Push the intents
211 + if (isElectedLeader && isActivatedLeader) {
212 + log.debug("Submitting all SDN-IP Peer Intents...");
213 + // TODO: We should use a single Intent batch operation
214 + for (Intent intent : intents) {
215 + intentService.submit(intent);
216 + }
217 + }
218 + }
219 + }
220 +
221 + /**
175 * Submits a multi-point-to-single-point intent. 222 * Submits a multi-point-to-single-point intent.
176 * 223 *
177 * @param prefix the IPv4 matching prefix for the intent to submit 224 * @param prefix the IPv4 matching prefix for the intent to submit
...@@ -180,14 +227,21 @@ public class IntentSynchronizer { ...@@ -180,14 +227,21 @@ public class IntentSynchronizer {
180 void submitRouteIntent(Ip4Prefix prefix, 227 void submitRouteIntent(Ip4Prefix prefix,
181 MultiPointToSinglePointIntent intent) { 228 MultiPointToSinglePointIntent intent) {
182 synchronized (this) { 229 synchronized (this) {
230 + MultiPointToSinglePointIntent oldIntent =
231 + routeIntents.put(prefix, intent);
232 +
183 if (isElectedLeader && isActivatedLeader) { 233 if (isElectedLeader && isActivatedLeader) {
184 log.debug("Intent installation: adding Intent for prefix: {}", 234 log.debug("Intent installation: adding Intent for prefix: {}",
185 prefix); 235 prefix);
236 + if (oldIntent != null) {
237 + //
238 + // TODO: Short-term solution to explicitly withdraw
239 + // instead of using "replace" operation.
240 + //
241 + intentService.withdraw(oldIntent);
242 + }
186 intentService.submit(intent); 243 intentService.submit(intent);
187 } 244 }
188 -
189 - // Maintain the Intent
190 - pushedRouteIntents.put(prefix, intent);
191 } 245 }
192 } 246 }
193 247
...@@ -199,11 +253,11 @@ public class IntentSynchronizer { ...@@ -199,11 +253,11 @@ public class IntentSynchronizer {
199 void withdrawRouteIntent(Ip4Prefix prefix) { 253 void withdrawRouteIntent(Ip4Prefix prefix) {
200 synchronized (this) { 254 synchronized (this) {
201 MultiPointToSinglePointIntent intent = 255 MultiPointToSinglePointIntent intent =
202 - pushedRouteIntents.remove(prefix); 256 + routeIntents.remove(prefix);
203 257
204 if (intent == null) { 258 if (intent == null) {
205 - log.debug("There is no intent in pushedRouteIntents to delete " + 259 + log.debug("There is no Intent in routeIntents to " +
206 - "for prefix: {}", prefix); 260 + "delete for prefix: {}", prefix);
207 return; 261 return;
208 } 262 }
209 263
...@@ -216,32 +270,56 @@ public class IntentSynchronizer { ...@@ -216,32 +270,56 @@ public class IntentSynchronizer {
216 } 270 }
217 271
218 /** 272 /**
219 - * Performs Intents Synchronization between the internally stored Route 273 + * Synchronize the in-memory Intents with the Intents in the Intent
220 - * Intents and the installed Route Intents. 274 + * framework.
221 */ 275 */
222 - private void syncIntents() { 276 + void synchronizeIntents() {
223 synchronized (this) { 277 synchronized (this) {
278 +
279 + Map<IntentKey, Intent> localIntents = new HashMap<>();
280 + Map<IntentKey, Intent> fetchedIntents = new HashMap<>();
281 + Collection<Intent> storeInMemoryIntents = new LinkedList<>();
282 + Collection<Intent> addIntents = new LinkedList<>();
283 + Collection<Intent> deleteIntents = new LinkedList<>();
284 +
224 if (!isElectedLeader) { 285 if (!isElectedLeader) {
225 return; // Nothing to do: not the leader anymore 286 return; // Nothing to do: not the leader anymore
226 } 287 }
227 - log.debug("Syncing SDN-IP Route Intents..."); 288 + log.debug("Syncing SDN-IP Intents...");
228 289
229 - Map<Ip4Prefix, MultiPointToSinglePointIntent> fetchedIntents = 290 + // Prepare the local intents
230 - new HashMap<>(); 291 + for (Intent intent : routeIntents.values()) {
292 + localIntents.put(new IntentKey(intent), intent);
293 + }
294 + for (Intent intent : peerIntents.values()) {
295 + localIntents.put(new IntentKey(intent), intent);
296 + }
231 297
232 - // 298 + // Fetch all intents for this application
233 - // Fetch all intents, and classify the Multi-Point-to-Point Intents
234 - // based on the matching prefix.
235 - //
236 for (Intent intent : intentService.getIntents()) { 299 for (Intent intent : intentService.getIntents()) {
237 - 300 + if (!intent.appId().equals(appId)) {
238 - if (!(intent instanceof MultiPointToSinglePointIntent)
239 - || !intent.appId().equals(appId)) {
240 continue; 301 continue;
241 } 302 }
303 + fetchedIntents.put(new IntentKey(intent), intent);
304 + }
305 +
306 + computeIntentsDelta(localIntents, fetchedIntents,
307 + storeInMemoryIntents, addIntents,
308 + deleteIntents);
309 +
310 + //
311 + // Perform the actions:
312 + // 1. Store in memory fetched intents that are same. Can be done
313 + // even if we are not the leader anymore
314 + // 2. Delete intents: check if the leader before the operation
315 + // 3. Add intents: check if the leader before the operation
316 + //
317 + for (Intent intent : storeInMemoryIntents) {
318 + // Store the intent in memory based on its type
319 + if (intent instanceof MultiPointToSinglePointIntent) {
242 MultiPointToSinglePointIntent mp2pIntent = 320 MultiPointToSinglePointIntent mp2pIntent =
243 (MultiPointToSinglePointIntent) intent; 321 (MultiPointToSinglePointIntent) intent;
244 - 322 + // Find the IP prefix
245 Criterion c = 323 Criterion c =
246 mp2pIntent.selector().getCriterion(Criterion.Type.IPV4_DST); 324 mp2pIntent.selector().getCriterion(Criterion.Type.IPV4_DST);
247 if (c != null && c instanceof IPCriterion) { 325 if (c != null && c instanceof IPCriterion) {
...@@ -251,156 +329,253 @@ public class IntentSynchronizer { ...@@ -251,156 +329,253 @@ public class IntentSynchronizer {
251 // TODO: For now we support only IPv4 329 // TODO: For now we support only IPv4
252 continue; 330 continue;
253 } 331 }
254 - fetchedIntents.put(ip4Prefix, mp2pIntent); 332 + log.debug("Intent synchronization: updating " +
333 + "in-memory Route Intent for prefix {}",
334 + ip4Prefix);
335 + routeIntents.put(ip4Prefix, mp2pIntent);
255 } else { 336 } else {
256 - log.warn("No IPV4_DST criterion found for intent {}", 337 + log.warn("No IPV4_DST criterion found for Intent {}",
257 mp2pIntent.id()); 338 mp2pIntent.id());
258 } 339 }
340 + continue;
341 + }
342 + if (intent instanceof PointToPointIntent) {
343 + PointToPointIntent p2pIntent = (PointToPointIntent) intent;
344 + log.debug("Intent synchronization: updating " +
345 + "in-memory Peer Intent {}", p2pIntent);
346 + peerIntents.put(new IntentKey(intent), p2pIntent);
347 + continue;
348 + }
349 + }
259 350
351 + // Withdraw Intents
352 + IntentOperations.Builder builder = IntentOperations.builder();
353 + for (Intent intent : deleteIntents) {
354 + builder.addWithdrawOperation(intent.id());
355 + log.debug("Intent synchronization: deleting Intent {}",
356 + intent);
260 } 357 }
358 + if (!isElectedLeader) {
359 + isActivatedLeader = false;
360 + return;
361 + }
362 + intentService.execute(builder.build());
363 +
364 + // Add Intents
365 + builder = IntentOperations.builder();
366 + for (Intent intent : addIntents) {
367 + builder.addSubmitOperation(intent);
368 + log.debug("Intent synchronization: adding Intent {}", intent);
369 + }
370 + if (!isElectedLeader) {
371 + isActivatedLeader = false;
372 + return;
373 + }
374 + intentService.execute(builder.build());
375 +
376 + if (isElectedLeader) {
377 + isActivatedLeader = true; // Allow push of Intents
378 + } else {
379 + isActivatedLeader = false;
380 + }
381 + log.debug("Syncing SDN-IP routes completed.");
382 + }
383 + }
384 +
385 + /**
386 + * Computes the delta in two sets of Intents: local in-memory Intents,
387 + * and intents fetched from the Intent framework.
388 + *
389 + * @param localIntents the local in-memory Intents
390 + * @param fetchedIntents the Intents fetched from the Intent framework
391 + * @param storeInMemoryIntents the Intents that should be stored in memory.
392 + * Note: This Collection must be allocated by the caller, and it will
393 + * be populated by this method.
394 + * @param addIntents the Intents that should be added to the Intent
395 + * framework. Note: This Collection must be allocated by the caller, and
396 + * it will be populated by this method.
397 + * @param deleteIntents the Intents that should be deleted from the Intent
398 + * framework. Note: This Collection must be allocated by the caller, and
399 + * it will be populated by this method.
400 + */
401 + private void computeIntentsDelta(
402 + final Map<IntentKey, Intent> localIntents,
403 + final Map<IntentKey, Intent> fetchedIntents,
404 + Collection<Intent> storeInMemoryIntents,
405 + Collection<Intent> addIntents,
406 + Collection<Intent> deleteIntents) {
261 407
262 // 408 //
263 - // Compare for each prefix the local IN-MEMORY Intents with the 409 + // Compute the deltas between the LOCAL in-memory Intents and the
264 // FETCHED Intents: 410 // FETCHED Intents:
265 - // - If the IN-MEMORY Intent is same as the FETCHED Intent, store 411 + // - If an Intent is in both the LOCAL and FETCHED sets:
266 - // the FETCHED Intent in the local memory (i.e., override the 412 + // If the FETCHED Intent is WITHDRAWING or WITHDRAWN, then
267 - // IN-MEMORY Intent) to preserve the original Intent ID 413 + // the LOCAL Intent should be added/installed; otherwise the
268 - // - if the IN-MEMORY Intent is not same as the FETCHED Intent, 414 + // FETCHED intent should be stored in the local memory
269 - // delete the FETCHED Intent, and push/install the IN-MEMORY 415 + // (i.e., override the LOCAL Intent) to preserve the original
270 - // Intent. 416 + // Intent ID.
271 - // - If there is an IN-MEMORY Intent for a prefix, but no FETCHED 417 + // - if a LOCAL Intent is not in the FETCHED set, then the LOCAL
272 - // Intent for same prefix, then push/install the IN-MEMORY 418 + // Intent should be added/installed.
273 - // Intent. 419 + // - If a FETCHED Intent is not in the LOCAL set, then the FETCHED
274 - // - If there is a FETCHED Intent for a prefix, but no IN-MEMORY 420 + // Intent should be deleted/withdrawn.
275 - // Intent for same prefix, then delete/withdraw the FETCHED
276 - // Intent.
277 // 421 //
278 - Collection<Pair<Ip4Prefix, MultiPointToSinglePointIntent>> 422 + for (Map.Entry<IntentKey, Intent> entry : localIntents.entrySet()) {
279 - storeInMemoryIntents = new LinkedList<>(); 423 + IntentKey intentKey = entry.getKey();
280 - Collection<Pair<Ip4Prefix, MultiPointToSinglePointIntent>> 424 + Intent localIntent = entry.getValue();
281 - addIntents = new LinkedList<>(); 425 + Intent fetchedIntent = fetchedIntents.get(intentKey);
282 - Collection<Pair<Ip4Prefix, MultiPointToSinglePointIntent>>
283 - deleteIntents = new LinkedList<>();
284 - for (Map.Entry<Ip4Prefix, MultiPointToSinglePointIntent> entry :
285 - pushedRouteIntents.entrySet()) {
286 - Ip4Prefix prefix = entry.getKey();
287 - MultiPointToSinglePointIntent inMemoryIntent =
288 - entry.getValue();
289 - MultiPointToSinglePointIntent fetchedIntent =
290 - fetchedIntents.get(prefix);
291 426
292 if (fetchedIntent == null) { 427 if (fetchedIntent == null) {
293 // 428 //
294 - // No FETCHED Intent for same prefix: push the IN-MEMORY 429 + // No FETCHED Intent found: push the LOCAL Intent.
295 - // Intent.
296 // 430 //
297 - addIntents.add(Pair.of(prefix, inMemoryIntent)); 431 + addIntents.add(localIntent);
298 continue; 432 continue;
299 } 433 }
300 434
301 - IntentState state = intentService.getIntentState(fetchedIntent.id()); 435 + IntentState state =
436 + intentService.getIntentState(fetchedIntent.id());
302 if (state == IntentState.WITHDRAWING || 437 if (state == IntentState.WITHDRAWING ||
303 state == IntentState.WITHDRAWN) { 438 state == IntentState.WITHDRAWN) {
304 // The intent has been withdrawn but according to our route 439 // The intent has been withdrawn but according to our route
305 // table it should be installed. We'll reinstall it. 440 // table it should be installed. We'll reinstall it.
306 - addIntents.add(Pair.of(prefix, inMemoryIntent)); 441 + addIntents.add(localIntent);
307 - } 442 + continue;
308 -
309 - //
310 - // If IN-MEMORY Intent is same as the FETCHED Intent,
311 - // store the FETCHED Intent in the local memory.
312 - //
313 - if (compareMultiPointToSinglePointIntents(inMemoryIntent,
314 - fetchedIntent)) {
315 - storeInMemoryIntents.add(Pair.of(prefix, fetchedIntent));
316 - } else {
317 - //
318 - // The IN-MEMORY Intent is not same as the FETCHED Intent,
319 - // hence delete the FETCHED Intent, and install the
320 - // IN-MEMORY Intent.
321 - //
322 - deleteIntents.add(Pair.of(prefix, fetchedIntent));
323 - addIntents.add(Pair.of(prefix, inMemoryIntent));
324 } 443 }
325 - fetchedIntents.remove(prefix); 444 + storeInMemoryIntents.add(fetchedIntent);
326 } 445 }
327 446
328 - // 447 + for (Map.Entry<IntentKey, Intent> entry : fetchedIntents.entrySet()) {
329 - // Any remaining FETCHED Intents have to be deleted/withdrawn 448 + IntentKey intentKey = entry.getKey();
330 - // 449 + Intent fetchedIntent = entry.getValue();
331 - for (Map.Entry<Ip4Prefix, MultiPointToSinglePointIntent> entry : 450 + Intent localIntent = localIntents.get(intentKey);
332 - fetchedIntents.entrySet()) { 451 +
333 - Ip4Prefix prefix = entry.getKey(); 452 + if (localIntent != null) {
334 - MultiPointToSinglePointIntent fetchedIntent = entry.getValue(); 453 + continue;
335 - deleteIntents.add(Pair.of(prefix, fetchedIntent));
336 } 454 }
337 455
338 - // 456 + IntentState state =
339 - // Perform the actions: 457 + intentService.getIntentState(fetchedIntent.id());
340 - // 1. Store in memory fetched intents that are same. Can be done 458 + if (state == IntentState.WITHDRAWING ||
341 - // even if we are not the leader anymore 459 + state == IntentState.WITHDRAWN) {
342 - // 2. Delete intents: check if the leader before each operation 460 + // Nothing to do. The intent has been already withdrawn.
343 - // 3. Add intents: check if the leader before each operation 461 + continue;
344 - //
345 - for (Pair<Ip4Prefix, MultiPointToSinglePointIntent> pair :
346 - storeInMemoryIntents) {
347 - Ip4Prefix prefix = pair.getLeft();
348 - MultiPointToSinglePointIntent intent = pair.getRight();
349 - log.debug("Intent synchronization: updating in-memory " +
350 - "Intent for prefix: {}", prefix);
351 - pushedRouteIntents.put(prefix, intent);
352 } 462 }
353 // 463 //
354 - isActivatedLeader = true; // Allow push of Intents 464 + // No LOCAL Intent found: delete/withdraw the FETCHED Intent.
355 - for (Pair<Ip4Prefix, MultiPointToSinglePointIntent> pair :
356 - deleteIntents) {
357 - Ip4Prefix prefix = pair.getLeft();
358 - MultiPointToSinglePointIntent intent = pair.getRight();
359 - if (!isElectedLeader) {
360 - isActivatedLeader = false;
361 - return;
362 - }
363 - log.debug("Intent synchronization: deleting Intent for " +
364 - "prefix: {}", prefix);
365 - intentService.withdraw(intent);
366 - }
367 // 465 //
368 - for (Pair<Ip4Prefix, MultiPointToSinglePointIntent> pair : 466 + deleteIntents.add(fetchedIntent);
369 - addIntents) {
370 - Ip4Prefix prefix = pair.getLeft();
371 - MultiPointToSinglePointIntent intent = pair.getRight();
372 - if (!isElectedLeader) {
373 - isActivatedLeader = false;
374 - return;
375 } 467 }
376 - log.debug("Intent synchronization: adding Intent for " +
377 - "prefix: {}", prefix);
378 - intentService.submit(intent);
379 } 468 }
380 - if (!isElectedLeader) { 469 +
381 - isActivatedLeader = false; 470 + /**
382 - } 471 + * Helper class that can be used to compute the key for an Intent by
383 - log.debug("Syncing SDN-IP routes completed."); 472 + * by excluding the Intent ID.
473 + */
474 + static final class IntentKey {
475 + private final Intent intent;
476 +
477 + /**
478 + * Constructor.
479 + *
480 + * @param intent the intent to use
481 + */
482 + IntentKey(Intent intent) {
483 + checkArgument((intent instanceof MultiPointToSinglePointIntent) ||
484 + (intent instanceof PointToPointIntent),
485 + "Intent type not recognized", intent);
486 + this.intent = intent;
384 } 487 }
488 +
489 + /**
490 + * Compares two Multi-Point to Single-Point Intents whether they
491 + * represent same logical intention.
492 + *
493 + * @param intent1 the first Intent to compare
494 + * @param intent2 the second Intent to compare
495 + * @return true if both Intents represent same logical intention,
496 + * otherwise false
497 + */
498 + static boolean equalIntents(MultiPointToSinglePointIntent intent1,
499 + MultiPointToSinglePointIntent intent2) {
500 + return Objects.equals(intent1.appId(), intent2.appId()) &&
501 + Objects.equals(intent1.selector(), intent2.selector()) &&
502 + Objects.equals(intent1.treatment(), intent2.treatment()) &&
503 + Objects.equals(intent1.ingressPoints(), intent2.ingressPoints()) &&
504 + Objects.equals(intent1.egressPoint(), intent2.egressPoint());
385 } 505 }
386 506
387 /** 507 /**
388 - * Compares two Multi-point to Single Point Intents whether they represent 508 + * Compares two Point-to-Point Intents whether they represent
389 * same logical intention. 509 * same logical intention.
390 * 510 *
391 * @param intent1 the first Intent to compare 511 * @param intent1 the first Intent to compare
392 * @param intent2 the second Intent to compare 512 * @param intent2 the second Intent to compare
393 - * @return true if both Intents represent same logical intention, otherwise 513 + * @return true if both Intents represent same logical intention,
394 - * false 514 + * otherwise false
395 */ 515 */
396 - private boolean compareMultiPointToSinglePointIntents( 516 + static boolean equalIntents(PointToPointIntent intent1,
397 - MultiPointToSinglePointIntent intent1, 517 + PointToPointIntent intent2) {
398 - MultiPointToSinglePointIntent intent2) { 518 + return Objects.equals(intent1.appId(), intent2.appId()) &&
519 + Objects.equals(intent1.selector(), intent2.selector()) &&
520 + Objects.equals(intent1.treatment(), intent2.treatment()) &&
521 + Objects.equals(intent1.ingressPoint(), intent2.ingressPoint()) &&
522 + Objects.equals(intent1.egressPoint(), intent2.egressPoint());
523 + }
399 524
400 - return Objects.equal(intent1.appId(), intent2.appId()) && 525 + @Override
401 - Objects.equal(intent1.selector(), intent2.selector()) && 526 + public int hashCode() {
402 - Objects.equal(intent1.treatment(), intent2.treatment()) && 527 + if (intent instanceof PointToPointIntent) {
403 - Objects.equal(intent1.ingressPoints(), intent2.ingressPoints()) && 528 + PointToPointIntent p2pIntent = (PointToPointIntent) intent;
404 - Objects.equal(intent1.egressPoint(), intent2.egressPoint()); 529 + return Objects.hash(p2pIntent.appId(),
530 + p2pIntent.resources(),
531 + p2pIntent.selector(),
532 + p2pIntent.treatment(),
533 + p2pIntent.constraints(),
534 + p2pIntent.ingressPoint(),
535 + p2pIntent.egressPoint());
536 + }
537 + if (intent instanceof MultiPointToSinglePointIntent) {
538 + MultiPointToSinglePointIntent m2pIntent =
539 + (MultiPointToSinglePointIntent) intent;
540 + return Objects.hash(m2pIntent.appId(),
541 + m2pIntent.resources(),
542 + m2pIntent.selector(),
543 + m2pIntent.treatment(),
544 + m2pIntent.constraints(),
545 + m2pIntent.ingressPoints(),
546 + m2pIntent.egressPoint());
547 + }
548 + checkArgument(false, "Intent type not recognized", intent);
549 + return 0;
550 + }
551 +
552 + @Override
553 + public boolean equals(Object obj) {
554 + if (this == obj) {
555 + return true;
556 + }
557 + if ((obj == null) || (!(obj instanceof IntentKey))) {
558 + return false;
559 + }
560 + IntentKey other = (IntentKey) obj;
561 +
562 + if (this.intent instanceof PointToPointIntent) {
563 + if (!(other.intent instanceof PointToPointIntent)) {
564 + return false;
565 + }
566 + return equalIntents((PointToPointIntent) this.intent,
567 + (PointToPointIntent) other.intent);
568 + }
569 + if (this.intent instanceof MultiPointToSinglePointIntent) {
570 + if (!(other.intent instanceof MultiPointToSinglePointIntent)) {
571 + return false;
572 + }
573 + return equalIntents(
574 + (MultiPointToSinglePointIntent) this.intent,
575 + (MultiPointToSinglePointIntent) other.intent);
576 + }
577 + checkArgument(false, "Intent type not recognized", intent);
578 + return false;
579 + }
405 } 580 }
406 } 581 }
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
16 package org.onlab.onos.sdnip; 16 package org.onlab.onos.sdnip;
17 17
18 import java.util.ArrayList; 18 import java.util.ArrayList;
19 +import java.util.Collection;
19 import java.util.List; 20 import java.util.List;
20 21
21 import org.onlab.onos.core.ApplicationId; 22 import org.onlab.onos.core.ApplicationId;
...@@ -24,8 +25,6 @@ import org.onlab.onos.net.flow.DefaultTrafficSelector; ...@@ -24,8 +25,6 @@ import org.onlab.onos.net.flow.DefaultTrafficSelector;
24 import org.onlab.onos.net.flow.DefaultTrafficTreatment; 25 import org.onlab.onos.net.flow.DefaultTrafficTreatment;
25 import org.onlab.onos.net.flow.TrafficSelector; 26 import org.onlab.onos.net.flow.TrafficSelector;
26 import org.onlab.onos.net.flow.TrafficTreatment; 27 import org.onlab.onos.net.flow.TrafficTreatment;
27 -import org.onlab.onos.net.intent.Intent;
28 -import org.onlab.onos.net.intent.IntentService;
29 import org.onlab.onos.net.intent.PointToPointIntent; 28 import org.onlab.onos.net.intent.PointToPointIntent;
30 import org.onlab.onos.sdnip.bgp.BgpConstants; 29 import org.onlab.onos.sdnip.bgp.BgpConstants;
31 import org.onlab.onos.sdnip.config.BgpPeer; 30 import org.onlab.onos.sdnip.config.BgpPeer;
...@@ -48,9 +47,9 @@ public class PeerConnectivityManager { ...@@ -48,9 +47,9 @@ public class PeerConnectivityManager {
48 private static final Logger log = LoggerFactory.getLogger( 47 private static final Logger log = LoggerFactory.getLogger(
49 PeerConnectivityManager.class); 48 PeerConnectivityManager.class);
50 49
50 + private final IntentSynchronizer intentSynchronizer;
51 private final SdnIpConfigService configService; 51 private final SdnIpConfigService configService;
52 private final InterfaceService interfaceService; 52 private final InterfaceService interfaceService;
53 - private final IntentService intentService;
54 53
55 private final ApplicationId appId; 54 private final ApplicationId appId;
56 55
...@@ -58,18 +57,18 @@ public class PeerConnectivityManager { ...@@ -58,18 +57,18 @@ public class PeerConnectivityManager {
58 * Creates a new PeerConnectivityManager. 57 * Creates a new PeerConnectivityManager.
59 * 58 *
60 * @param appId the application ID 59 * @param appId the application ID
60 + * @param intentSynchronizer the intent synchronizer
61 * @param configService the SDN-IP config service 61 * @param configService the SDN-IP config service
62 * @param interfaceService the interface service 62 * @param interfaceService the interface service
63 - * @param intentService the intent service
64 */ 63 */
65 public PeerConnectivityManager(ApplicationId appId, 64 public PeerConnectivityManager(ApplicationId appId,
65 + IntentSynchronizer intentSynchronizer,
66 SdnIpConfigService configService, 66 SdnIpConfigService configService,
67 - InterfaceService interfaceService, 67 + InterfaceService interfaceService) {
68 - IntentService intentService) {
69 this.appId = appId; 68 this.appId = appId;
69 + this.intentSynchronizer = intentSynchronizer;
70 this.configService = configService; 70 this.configService = configService;
71 this.interfaceService = interfaceService; 71 this.interfaceService = interfaceService;
72 - this.intentService = intentService;
73 } 72 }
74 73
75 /** 74 /**
...@@ -107,6 +106,8 @@ public class PeerConnectivityManager { ...@@ -107,6 +106,8 @@ public class PeerConnectivityManager {
107 * {@link BgpSpeaker}s and all external {@link BgpPeer}s. 106 * {@link BgpSpeaker}s and all external {@link BgpPeer}s.
108 */ 107 */
109 private void setUpConnectivity() { 108 private void setUpConnectivity() {
109 + List<PointToPointIntent> intents = new ArrayList<>();
110 +
110 for (BgpSpeaker bgpSpeaker : configService.getBgpSpeakers() 111 for (BgpSpeaker bgpSpeaker : configService.getBgpSpeakers()
111 .values()) { 112 .values()) {
112 log.debug("Start to set up BGP paths for BGP speaker: {}", 113 log.debug("Start to set up BGP paths for BGP speaker: {}",
...@@ -117,9 +118,12 @@ public class PeerConnectivityManager { ...@@ -117,9 +118,12 @@ public class PeerConnectivityManager {
117 log.debug("Start to set up BGP paths between BGP speaker: {} " 118 log.debug("Start to set up BGP paths between BGP speaker: {} "
118 + "to BGP peer: {}", bgpSpeaker, bgpPeer); 119 + "to BGP peer: {}", bgpSpeaker, bgpPeer);
119 120
120 - buildPeerIntents(bgpSpeaker, bgpPeer); 121 + intents.addAll(buildPeerIntents(bgpSpeaker, bgpPeer));
121 } 122 }
122 } 123 }
124 +
125 + // Submit all the intents.
126 + intentSynchronizer.submitPeerIntents(intents);
123 } 127 }
124 128
125 /** 129 /**
...@@ -128,9 +132,12 @@ public class PeerConnectivityManager { ...@@ -128,9 +132,12 @@ public class PeerConnectivityManager {
128 * 132 *
129 * @param bgpSpeaker the BGP speaker 133 * @param bgpSpeaker the BGP speaker
130 * @param bgpPeer the BGP peer 134 * @param bgpPeer the BGP peer
135 + * @return the intents to install
131 */ 136 */
132 - private void buildPeerIntents(BgpSpeaker bgpSpeaker, BgpPeer bgpPeer) { 137 + private Collection<PointToPointIntent> buildPeerIntents(
133 - List<Intent> intents = new ArrayList<Intent>(); 138 + BgpSpeaker bgpSpeaker,
139 + BgpPeer bgpPeer) {
140 + List<PointToPointIntent> intents = new ArrayList<>();
134 141
135 ConnectPoint bgpdConnectPoint = bgpSpeaker.connectPoint(); 142 ConnectPoint bgpdConnectPoint = bgpSpeaker.connectPoint();
136 143
...@@ -142,7 +149,7 @@ public class PeerConnectivityManager { ...@@ -142,7 +149,7 @@ public class PeerConnectivityManager {
142 149
143 if (peerInterface == null) { 150 if (peerInterface == null) {
144 log.error("No interface found for peer {}", bgpPeer.ipAddress()); 151 log.error("No interface found for peer {}", bgpPeer.ipAddress());
145 - return; 152 + return intents;
146 } 153 }
147 154
148 IpAddress bgpdAddress = null; 155 IpAddress bgpdAddress = null;
...@@ -156,7 +163,7 @@ public class PeerConnectivityManager { ...@@ -156,7 +163,7 @@ public class PeerConnectivityManager {
156 if (bgpdAddress == null) { 163 if (bgpdAddress == null) {
157 log.debug("No IP address found for peer {} on interface {}", 164 log.debug("No IP address found for peer {} on interface {}",
158 bgpPeer, bgpPeer.connectPoint()); 165 bgpPeer, bgpPeer.connectPoint());
159 - return; 166 + return intents;
160 } 167 }
161 168
162 IpAddress bgpdPeerAddress = bgpPeer.ipAddress(); 169 IpAddress bgpdPeerAddress = bgpPeer.ipAddress();
...@@ -231,11 +238,7 @@ public class PeerConnectivityManager { ...@@ -231,11 +238,7 @@ public class PeerConnectivityManager {
231 intents.add(new PointToPointIntent(appId, selector, treatment, 238 intents.add(new PointToPointIntent(appId, selector, treatment,
232 bgpdPeerConnectPoint, bgpdConnectPoint)); 239 bgpdPeerConnectPoint, bgpdConnectPoint));
233 240
234 - // Submit all the intents. 241 + return intents;
235 - // TODO submit as a batch
236 - for (Intent intent : intents) {
237 - intentService.submit(intent);
238 - }
239 } 242 }
240 243
241 /** 244 /**
...@@ -249,7 +252,8 @@ public class PeerConnectivityManager { ...@@ -249,7 +252,8 @@ public class PeerConnectivityManager {
249 * @return the new traffic selector 252 * @return the new traffic selector
250 */ 253 */
251 private TrafficSelector buildSelector(byte ipProto, IpAddress srcIp, 254 private TrafficSelector buildSelector(byte ipProto, IpAddress srcIp,
252 - IpAddress dstIp, Short srcTcpPort, Short dstTcpPort) { 255 + IpAddress dstIp, Short srcTcpPort,
256 + Short dstTcpPort) {
253 TrafficSelector.Builder builder = DefaultTrafficSelector.builder() 257 TrafficSelector.Builder builder = DefaultTrafficSelector.builder()
254 .matchEthType(Ethernet.TYPE_IPV4) 258 .matchEthType(Ethernet.TYPE_IPV4)
255 .matchIPProtocol(ipProto) 259 .matchIPProtocol(ipProto)
......
...@@ -92,18 +92,19 @@ public class Router implements RouteListener { ...@@ -92,18 +92,19 @@ public class Router implements RouteListener {
92 * 92 *
93 * @param appId the application ID 93 * @param appId the application ID
94 * @param intentSynchronizer the intent synchronizer 94 * @param intentSynchronizer the intent synchronizer
95 - * @param hostService the host service
96 * @param configService the configuration service 95 * @param configService the configuration service
97 * @param interfaceService the interface service 96 * @param interfaceService the interface service
97 + * @param hostService the host service
98 */ 98 */
99 public Router(ApplicationId appId, IntentSynchronizer intentSynchronizer, 99 public Router(ApplicationId appId, IntentSynchronizer intentSynchronizer,
100 - HostService hostService, SdnIpConfigService configService, 100 + SdnIpConfigService configService,
101 - InterfaceService interfaceService) { 101 + InterfaceService interfaceService,
102 + HostService hostService) {
102 this.appId = appId; 103 this.appId = appId;
103 this.intentSynchronizer = intentSynchronizer; 104 this.intentSynchronizer = intentSynchronizer;
104 - this.hostService = hostService;
105 this.configService = configService; 105 this.configService = configService;
106 this.interfaceService = interfaceService; 106 this.interfaceService = interfaceService;
107 + this.hostService = hostService;
107 108
108 this.hostListener = new InternalHostListener(); 109 this.hostListener = new InternalHostListener();
109 110
......
...@@ -18,8 +18,6 @@ package org.onlab.onos.sdnip; ...@@ -18,8 +18,6 @@ package org.onlab.onos.sdnip;
18 import static org.slf4j.LoggerFactory.getLogger; 18 import static org.slf4j.LoggerFactory.getLogger;
19 19
20 import java.util.Collection; 20 import java.util.Collection;
21 -import java.util.concurrent.ExecutorService;
22 -import java.util.concurrent.Executors;
23 21
24 import org.apache.felix.scr.annotations.Activate; 22 import org.apache.felix.scr.annotations.Activate;
25 import org.apache.felix.scr.annotations.Component; 23 import org.apache.felix.scr.annotations.Component;
...@@ -27,6 +25,11 @@ import org.apache.felix.scr.annotations.Deactivate; ...@@ -27,6 +25,11 @@ import org.apache.felix.scr.annotations.Deactivate;
27 import org.apache.felix.scr.annotations.Reference; 25 import org.apache.felix.scr.annotations.Reference;
28 import org.apache.felix.scr.annotations.ReferenceCardinality; 26 import org.apache.felix.scr.annotations.ReferenceCardinality;
29 import org.apache.felix.scr.annotations.Service; 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;
30 import org.onlab.onos.core.ApplicationId; 33 import org.onlab.onos.core.ApplicationId;
31 import org.onlab.onos.core.CoreService; 34 import org.onlab.onos.core.CoreService;
32 import org.onlab.onos.net.host.HostService; 35 import org.onlab.onos.net.host.HostService;
...@@ -35,11 +38,8 @@ import org.onlab.onos.sdnip.bgp.BgpRouteEntry; ...@@ -35,11 +38,8 @@ import org.onlab.onos.sdnip.bgp.BgpRouteEntry;
35 import org.onlab.onos.sdnip.bgp.BgpSession; 38 import org.onlab.onos.sdnip.bgp.BgpSession;
36 import org.onlab.onos.sdnip.bgp.BgpSessionManager; 39 import org.onlab.onos.sdnip.bgp.BgpSessionManager;
37 import org.onlab.onos.sdnip.config.SdnIpConfigReader; 40 import org.onlab.onos.sdnip.config.SdnIpConfigReader;
38 -import org.onlab.onos.store.service.Lock;
39 -import org.onlab.onos.store.service.LockService;
40 -import org.slf4j.Logger;
41 41
42 -import com.google.common.util.concurrent.ThreadFactoryBuilder; 42 +import org.slf4j.Logger;
43 43
44 /** 44 /**
45 * Component for the SDN-IP peering application. 45 * Component for the SDN-IP peering application.
...@@ -65,55 +65,49 @@ public class SdnIp implements SdnIpService { ...@@ -65,55 +65,49 @@ public class SdnIp implements SdnIpService {
65 protected HostService hostService; 65 protected HostService hostService;
66 66
67 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 67 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
68 - protected LockService lockService; 68 + protected ClusterService clusterService;
69 +
70 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
71 + protected LeadershipService leadershipService;
69 72
70 private IntentSynchronizer intentSynchronizer; 73 private IntentSynchronizer intentSynchronizer;
71 private SdnIpConfigReader config; 74 private SdnIpConfigReader config;
72 private PeerConnectivityManager peerConnectivity; 75 private PeerConnectivityManager peerConnectivity;
73 private Router router; 76 private Router router;
74 private BgpSessionManager bgpSessionManager; 77 private BgpSessionManager bgpSessionManager;
75 - 78 + private LeadershipEventListener leadershipEventListener =
76 - private ExecutorService leaderElectionExecutor; 79 + new InnerLeadershipEventListener();
77 - private Lock leaderLock; 80 + ApplicationId appId;
78 - private volatile boolean isShutdown = true; 81 + private ControllerNode localControllerNode;
79 82
80 @Activate 83 @Activate
81 protected void activate() { 84 protected void activate() {
82 log.info("SDN-IP started"); 85 log.info("SDN-IP started");
83 - isShutdown = false;
84 86
85 - ApplicationId appId = coreService.registerApplication(SDN_IP_APP); 87 + appId = coreService.registerApplication(SDN_IP_APP);
86 config = new SdnIpConfigReader(); 88 config = new SdnIpConfigReader();
87 config.init(); 89 config.init();
88 90
91 + localControllerNode = clusterService.getLocalNode();
92 +
89 InterfaceService interfaceService = 93 InterfaceService interfaceService =
90 new HostToInterfaceAdaptor(hostService); 94 new HostToInterfaceAdaptor(hostService);
91 95
92 intentSynchronizer = new IntentSynchronizer(appId, intentService); 96 intentSynchronizer = new IntentSynchronizer(appId, intentService);
93 intentSynchronizer.start(); 97 intentSynchronizer.start();
94 98
95 - peerConnectivity = new PeerConnectivityManager(appId, config, 99 + peerConnectivity = new PeerConnectivityManager(appId,
96 - interfaceService, intentService); 100 + intentSynchronizer,
101 + config,
102 + interfaceService);
97 peerConnectivity.start(); 103 peerConnectivity.start();
98 104
99 - router = new Router(appId, intentSynchronizer, hostService, config, 105 + router = new Router(appId, intentSynchronizer, config,
100 - interfaceService); 106 + interfaceService, hostService);
101 router.start(); 107 router.start();
102 108
103 - leaderLock = lockService.create(SDN_IP_APP + "/sdnIpLeaderLock"); 109 + leadershipService.addListener(leadershipEventListener);
104 - leaderElectionExecutor = Executors.newSingleThreadExecutor( 110 + leadershipService.runForLeadership(appId.name());
105 - new ThreadFactoryBuilder()
106 - .setNameFormat("sdnip-leader-election-%d").build());
107 - leaderElectionExecutor.execute(new Runnable() {
108 - @Override
109 - public void run() {
110 - doLeaderElectionThread();
111 - }
112 - });
113 -
114 - // Manually set the instance as the leader to allow testing
115 - // TODO change this when we get a leader election
116 - // intentSynchronizer.leaderChanged(true);
117 111
118 bgpSessionManager = new BgpSessionManager(router); 112 bgpSessionManager = new BgpSessionManager(router);
119 // TODO: the local BGP listen port number should be configurable 113 // TODO: the local BGP listen port number should be configurable
...@@ -124,17 +118,16 @@ public class SdnIp implements SdnIpService { ...@@ -124,17 +118,16 @@ public class SdnIp implements SdnIpService {
124 118
125 @Deactivate 119 @Deactivate
126 protected void deactivate() { 120 protected void deactivate() {
127 - isShutdown = true;
128 121
129 bgpSessionManager.stop(); 122 bgpSessionManager.stop();
130 router.stop(); 123 router.stop();
131 peerConnectivity.stop(); 124 peerConnectivity.stop();
132 intentSynchronizer.stop(); 125 intentSynchronizer.stop();
133 126
134 - // Stop the thread(s) 127 + leadershipService.withdraw(appId.name());
135 - leaderElectionExecutor.shutdownNow(); 128 + leadershipService.removeListener(leadershipEventListener);
136 129
137 - log.info("Stopped"); 130 + log.info("SDN-IP Stopped");
138 } 131 }
139 132
140 @Override 133 @Override
...@@ -162,63 +155,38 @@ public class SdnIp implements SdnIpService { ...@@ -162,63 +155,38 @@ public class SdnIp implements SdnIpService {
162 } 155 }
163 156
164 /** 157 /**
165 - * Performs the leader election. 158 + * A listener for Leadership Events.
166 */ 159 */
167 - private void doLeaderElectionThread() { 160 + private class InnerLeadershipEventListener
161 + implements LeadershipEventListener {
168 162
169 - // 163 + @Override
170 - // Try to acquire the lock and keep extending it until the instance 164 + public void event(LeadershipEvent event) {
171 - // is shutdown. 165 + log.debug("Leadership Event: time = {} type = {} event = {}",
172 - // 166 + event.time(), event.type(), event);
173 - while (!isShutdown) {
174 - log.debug("SDN-IP Leader Election begin");
175 167
176 - // Block until it becomes the leader 168 + if (!event.subject().topic().equals(appId.name())) {
177 - try { 169 + return; // Not our topic: ignore
178 - leaderLock.lock(LEASE_DURATION_MS); 170 + }
171 + if (!event.subject().leader().id().equals(
172 + localControllerNode.id())) {
173 + return; // The event is not about this instance: ignore
174 + }
179 175
180 - // This instance is the leader 176 + switch (event.type()) {
177 + case LEADER_ELECTED:
181 log.info("SDN-IP Leader Elected"); 178 log.info("SDN-IP Leader Elected");
182 intentSynchronizer.leaderChanged(true); 179 intentSynchronizer.leaderChanged(true);
183 - 180 + break;
184 - // Keep extending the expiration until shutdown 181 + case LEADER_BOOTED:
185 - int extensionFailedCountdown = LEASE_EXTEND_RETRY_MAX - 1; 182 + log.info("SDN-IP Leader Lost Election");
186 -
187 - //
188 - // Keep periodically extending the lock expiration.
189 - // If there are multiple back-to-back failures to extend (with
190 - // extra sleep time between retrials), then release the lock.
191 - //
192 - while (!isShutdown) {
193 - Thread.sleep(LEASE_DURATION_MS / LEASE_EXTEND_RETRY_MAX);
194 - if (leaderLock.extendExpiration(LEASE_DURATION_MS)) {
195 - log.trace("SDN-IP Leader Extended");
196 - extensionFailedCountdown = LEASE_EXTEND_RETRY_MAX;
197 - } else {
198 - log.debug("SDN-IP Leader Cannot Extend Election");
199 - if (!leaderLock.isLocked()) {
200 - log.debug("SDN-IP Leader Lock Lost");
201 - intentSynchronizer.leaderChanged(false);
202 - break; // Try again to get the lock
203 - }
204 - extensionFailedCountdown--;
205 - if (extensionFailedCountdown <= 0) {
206 - // Failed too many times to extend.
207 - // Release the lock.
208 - log.debug("SDN-IP Leader Lock Released");
209 intentSynchronizer.leaderChanged(false); 183 intentSynchronizer.leaderChanged(false);
210 - leaderLock.unlock(); 184 + break;
211 - break; // Try again to get the lock 185 + case LEADER_REELECTED:
212 - } 186 + break;
213 - } 187 + default:
188 + break;
214 } 189 }
215 - } catch (InterruptedException e) {
216 - // Thread interrupted. Time to shutdown
217 - log.debug("SDN-IP Leader Interrupted");
218 } 190 }
219 } 191 }
220 - // If we reach here, the instance was shutdown
221 - intentSynchronizer.leaderChanged(false);
222 - leaderLock.unlock();
223 - }
224 } 192 }
......
...@@ -5,16 +5,23 @@ import static org.easymock.EasyMock.createMock; ...@@ -5,16 +5,23 @@ import static org.easymock.EasyMock.createMock;
5 import static org.easymock.EasyMock.expect; 5 import static org.easymock.EasyMock.expect;
6 import static org.easymock.EasyMock.expectLastCall; 6 import static org.easymock.EasyMock.expectLastCall;
7 import static org.easymock.EasyMock.replay; 7 import static org.easymock.EasyMock.replay;
8 +import static org.easymock.EasyMock.reportMatcher;
8 import static org.easymock.EasyMock.reset; 9 import static org.easymock.EasyMock.reset;
9 import static org.easymock.EasyMock.verify; 10 import static org.easymock.EasyMock.verify;
11 +import static org.hamcrest.Matchers.is;
10 import static org.junit.Assert.assertEquals; 12 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertFalse; 13 import static org.junit.Assert.assertFalse;
14 +import static org.junit.Assert.assertThat;
12 import static org.junit.Assert.assertTrue; 15 import static org.junit.Assert.assertTrue;
13 16
14 import java.util.HashSet; 17 import java.util.HashSet;
18 +import java.util.LinkedList;
19 +import java.util.List;
15 import java.util.Set; 20 import java.util.Set;
16 import java.util.concurrent.ConcurrentHashMap; 21 import java.util.concurrent.ConcurrentHashMap;
17 22
23 +import org.apache.commons.collections4.CollectionUtils;
24 +import org.easymock.IArgumentMatcher;
18 import org.junit.Before; 25 import org.junit.Before;
19 import org.junit.Test; 26 import org.junit.Test;
20 import org.onlab.junit.TestUtils; 27 import org.onlab.junit.TestUtils;
...@@ -35,10 +42,14 @@ import org.onlab.onos.net.host.HostListener; ...@@ -35,10 +42,14 @@ import org.onlab.onos.net.host.HostListener;
35 import org.onlab.onos.net.host.HostService; 42 import org.onlab.onos.net.host.HostService;
36 import org.onlab.onos.net.host.InterfaceIpAddress; 43 import org.onlab.onos.net.host.InterfaceIpAddress;
37 import org.onlab.onos.net.intent.Intent; 44 import org.onlab.onos.net.intent.Intent;
45 +import org.onlab.onos.net.intent.IntentId;
46 +import org.onlab.onos.net.intent.IntentOperation;
47 +import org.onlab.onos.net.intent.IntentOperations;
38 import org.onlab.onos.net.intent.IntentService; 48 import org.onlab.onos.net.intent.IntentService;
39 import org.onlab.onos.net.intent.IntentState; 49 import org.onlab.onos.net.intent.IntentState;
40 import org.onlab.onos.net.intent.MultiPointToSinglePointIntent; 50 import org.onlab.onos.net.intent.MultiPointToSinglePointIntent;
41 import org.onlab.onos.net.provider.ProviderId; 51 import org.onlab.onos.net.provider.ProviderId;
52 +import org.onlab.onos.sdnip.IntentSynchronizer.IntentKey;
42 import org.onlab.onos.sdnip.config.Interface; 53 import org.onlab.onos.sdnip.config.Interface;
43 import org.onlab.packet.Ethernet; 54 import org.onlab.packet.Ethernet;
44 import org.onlab.packet.IpAddress; 55 import org.onlab.packet.IpAddress;
...@@ -97,8 +108,8 @@ public class IntentSyncTest { ...@@ -97,8 +108,8 @@ public class IntentSyncTest {
97 intentService = createMock(IntentService.class); 108 intentService = createMock(IntentService.class);
98 109
99 intentSynchronizer = new IntentSynchronizer(APPID, intentService); 110 intentSynchronizer = new IntentSynchronizer(APPID, intentService);
100 - router = new Router(APPID, intentSynchronizer, 111 + router = new Router(APPID, intentSynchronizer, null, interfaceService,
101 - hostService, null, interfaceService); 112 + hostService);
102 } 113 }
103 114
104 /** 115 /**
...@@ -263,17 +274,16 @@ public class IntentSyncTest { ...@@ -263,17 +274,16 @@ public class IntentSyncTest {
263 // Compose a intent, which is equal to intent5 but the id is different. 274 // Compose a intent, which is equal to intent5 but the id is different.
264 MultiPointToSinglePointIntent intent5New = 275 MultiPointToSinglePointIntent intent5New =
265 staticIntentBuilder(intent5, routeEntry5, "00:00:00:00:00:01"); 276 staticIntentBuilder(intent5, routeEntry5, "00:00:00:00:00:01");
266 - assertTrue(TestUtils.callMethod(intentSynchronizer, 277 + assertThat(IntentSynchronizer.IntentKey.equalIntents(
267 - "compareMultiPointToSinglePointIntents", 278 + intent5, intent5New),
268 - new Class<?>[] {MultiPointToSinglePointIntent.class, 279 + is(true));
269 - MultiPointToSinglePointIntent.class},
270 - intent5, intent5New).equals(true));
271 assertFalse(intent5.equals(intent5New)); 280 assertFalse(intent5.equals(intent5New));
272 281
273 MultiPointToSinglePointIntent intent6 = intentBuilder( 282 MultiPointToSinglePointIntent intent6 = intentBuilder(
274 routeEntry6.prefix(), "00:00:00:00:00:01", SW1_ETH1); 283 routeEntry6.prefix(), "00:00:00:00:00:01", SW1_ETH1);
275 284
276 - // Set up the bgpRoutes and pushedRouteIntents fields in Router class 285 + // Set up the bgpRoutes field in Router class and routeIntents fields
286 + // in IntentSynchronizer class
277 InvertedRadixTree<RouteEntry> bgpRoutes = 287 InvertedRadixTree<RouteEntry> bgpRoutes =
278 new ConcurrentInvertedRadixTree<>( 288 new ConcurrentInvertedRadixTree<>(
279 new DefaultByteArrayNodeFactory()); 289 new DefaultByteArrayNodeFactory());
...@@ -292,15 +302,14 @@ public class IntentSyncTest { ...@@ -292,15 +302,14 @@ public class IntentSyncTest {
292 TestUtils.setField(router, "bgpRoutes", bgpRoutes); 302 TestUtils.setField(router, "bgpRoutes", bgpRoutes);
293 303
294 ConcurrentHashMap<Ip4Prefix, MultiPointToSinglePointIntent> 304 ConcurrentHashMap<Ip4Prefix, MultiPointToSinglePointIntent>
295 - pushedRouteIntents = new ConcurrentHashMap<>(); 305 + routeIntents = new ConcurrentHashMap<>();
296 - pushedRouteIntents.put(routeEntry1.prefix(), intent1); 306 + routeIntents.put(routeEntry1.prefix(), intent1);
297 - pushedRouteIntents.put(routeEntry3.prefix(), intent3); 307 + routeIntents.put(routeEntry3.prefix(), intent3);
298 - pushedRouteIntents.put(routeEntry4Update.prefix(), intent4Update); 308 + routeIntents.put(routeEntry4Update.prefix(), intent4Update);
299 - pushedRouteIntents.put(routeEntry5.prefix(), intent5New); 309 + routeIntents.put(routeEntry5.prefix(), intent5New);
300 - pushedRouteIntents.put(routeEntry6.prefix(), intent6); 310 + routeIntents.put(routeEntry6.prefix(), intent6);
301 - pushedRouteIntents.put(routeEntry7.prefix(), intent7); 311 + routeIntents.put(routeEntry7.prefix(), intent7);
302 - TestUtils.setField(intentSynchronizer, "pushedRouteIntents", 312 + TestUtils.setField(intentSynchronizer, "routeIntents", routeIntents);
303 - pushedRouteIntents);
304 313
305 // Set up expectation 314 // Set up expectation
306 reset(intentService); 315 reset(intentService);
...@@ -322,18 +331,26 @@ public class IntentSyncTest { ...@@ -322,18 +331,26 @@ public class IntentSyncTest {
322 .andReturn(IntentState.WITHDRAWING).anyTimes(); 331 .andReturn(IntentState.WITHDRAWING).anyTimes();
323 expect(intentService.getIntents()).andReturn(intents).anyTimes(); 332 expect(intentService.getIntents()).andReturn(intents).anyTimes();
324 333
325 - intentService.withdraw(intent2); 334 + IntentOperations.Builder builder = IntentOperations.builder();
326 - intentService.submit(intent3); 335 + builder.addWithdrawOperation(intent2.id());
327 - intentService.withdraw(intent4); 336 + builder.addWithdrawOperation(intent4.id());
328 - intentService.submit(intent4Update); 337 + intentService.execute(eqExceptId(builder.build()));
329 - intentService.submit(intent6); 338 +
330 - intentService.submit(intent7); 339 + builder = IntentOperations.builder();
340 + builder.addSubmitOperation(intent3);
341 + builder.addSubmitOperation(intent4Update);
342 + builder.addSubmitOperation(intent6);
343 + builder.addSubmitOperation(intent7);
344 + intentService.execute(eqExceptId(builder.build()));
331 replay(intentService); 345 replay(intentService);
332 346
333 // Start the test 347 // Start the test
334 intentSynchronizer.leaderChanged(true); 348 intentSynchronizer.leaderChanged(true);
335 - TestUtils.callMethod(intentSynchronizer, "syncIntents", 349 + /*
350 + TestUtils.callMethod(intentSynchronizer, "synchronizeIntents",
336 new Class<?>[] {}); 351 new Class<?>[] {});
352 + */
353 + intentSynchronizer.synchronizeIntents();
337 354
338 // Verify 355 // Verify
339 assertEquals(router.getRoutes().size(), 6); 356 assertEquals(router.getRoutes().size(), 6);
...@@ -343,12 +360,12 @@ public class IntentSyncTest { ...@@ -343,12 +360,12 @@ public class IntentSyncTest {
343 assertTrue(router.getRoutes().contains(routeEntry5)); 360 assertTrue(router.getRoutes().contains(routeEntry5));
344 assertTrue(router.getRoutes().contains(routeEntry6)); 361 assertTrue(router.getRoutes().contains(routeEntry6));
345 362
346 - assertEquals(intentSynchronizer.getPushedRouteIntents().size(), 6); 363 + assertEquals(intentSynchronizer.getRouteIntents().size(), 6);
347 - assertTrue(intentSynchronizer.getPushedRouteIntents().contains(intent1)); 364 + assertTrue(intentSynchronizer.getRouteIntents().contains(intent1));
348 - assertTrue(intentSynchronizer.getPushedRouteIntents().contains(intent3)); 365 + assertTrue(intentSynchronizer.getRouteIntents().contains(intent3));
349 - assertTrue(intentSynchronizer.getPushedRouteIntents().contains(intent4Update)); 366 + assertTrue(intentSynchronizer.getRouteIntents().contains(intent4Update));
350 - assertTrue(intentSynchronizer.getPushedRouteIntents().contains(intent5)); 367 + assertTrue(intentSynchronizer.getRouteIntents().contains(intent5));
351 - assertTrue(intentSynchronizer.getPushedRouteIntents().contains(intent6)); 368 + assertTrue(intentSynchronizer.getRouteIntents().contains(intent6));
352 369
353 verify(intentService); 370 verify(intentService);
354 } 371 }
...@@ -410,4 +427,129 @@ public class IntentSyncTest { ...@@ -410,4 +427,129 @@ public class IntentSyncTest {
410 "ingressPoints", intent.ingressPoints()); 427 "ingressPoints", intent.ingressPoints());
411 return intentNew; 428 return intentNew;
412 } 429 }
430 +
431 + /*
432 + * EasyMock matcher that matches {@link IntenOperations} but
433 + * ignores the {@link IntentId} when matching.
434 + * <p/>
435 + * The normal intent equals method tests that the intent IDs are equal,
436 + * however in these tests we can't know what the intent IDs will be in
437 + * advance, so we can't set up expected intents with the correct IDs. Thus,
438 + * the solution is to use an EasyMock matcher that verifies that all the
439 + * value properties of the provided intent match the expected values, but
440 + * ignores the intent ID when testing equality.
441 + */
442 + private static final class IdAgnosticIntentOperationsMatcher implements
443 + IArgumentMatcher {
444 +
445 + private final IntentOperations intentOperations;
446 + private String providedString;
447 +
448 + /**
449 + * Constructor taking the expected intent operations to match against.
450 + *
451 + * @param intentOperations the expected intent operations
452 + */
453 + public IdAgnosticIntentOperationsMatcher(
454 + IntentOperations intentOperations) {
455 + this.intentOperations = intentOperations;
456 + }
457 +
458 + @Override
459 + public void appendTo(StringBuffer strBuffer) {
460 + strBuffer.append("IntentOperationsMatcher unable to match: "
461 + + providedString);
462 + }
463 +
464 + @Override
465 + public boolean matches(Object object) {
466 + if (!(object instanceof IntentOperations)) {
467 + return false;
468 + }
469 +
470 + IntentOperations providedIntentOperations =
471 + (IntentOperations) object;
472 + providedString = providedIntentOperations.toString();
473 +
474 + List<IntentKey> thisSubmitIntents = new LinkedList<>();
475 + List<IntentId> thisWithdrawIntentIds = new LinkedList<>();
476 + List<IntentKey> thisReplaceIntents = new LinkedList<>();
477 + List<IntentKey> thisUpdateIntents = new LinkedList<>();
478 + List<IntentKey> providedSubmitIntents = new LinkedList<>();
479 + List<IntentId> providedWithdrawIntentIds = new LinkedList<>();
480 + List<IntentKey> providedReplaceIntents = new LinkedList<>();
481 + List<IntentKey> providedUpdateIntents = new LinkedList<>();
482 +
483 + extractIntents(intentOperations, thisSubmitIntents,
484 + thisWithdrawIntentIds, thisReplaceIntents,
485 + thisUpdateIntents);
486 + extractIntents(providedIntentOperations, providedSubmitIntents,
487 + providedWithdrawIntentIds, providedReplaceIntents,
488 + providedUpdateIntents);
489 +
490 + return CollectionUtils.isEqualCollection(thisSubmitIntents,
491 + providedSubmitIntents) &&
492 + CollectionUtils.isEqualCollection(thisWithdrawIntentIds,
493 + providedWithdrawIntentIds) &&
494 + CollectionUtils.isEqualCollection(thisUpdateIntents,
495 + providedUpdateIntents) &&
496 + CollectionUtils.isEqualCollection(thisReplaceIntents,
497 + providedReplaceIntents);
498 + }
499 +
500 + /**
501 + * Extracts the intents per operation type. Each intent is encapsulated
502 + * in IntentKey so it can be compared by excluding the Intent ID.
503 + *
504 + * @param intentOperations the container with the intent operations
505 + * to extract the intents from
506 + * @param submitIntents the SUBMIT intents
507 + * @param withdrawIntentIds the WITHDRAW intents IDs
508 + * @param replaceIntents the REPLACE intents
509 + * @param updateIntents the UPDATE intens
510 + */
511 + private void extractIntents(IntentOperations intentOperations,
512 + List<IntentKey> submitIntents,
513 + List<IntentId> withdrawIntentIds,
514 + List<IntentKey> replaceIntents,
515 + List<IntentKey> updateIntents) {
516 + for (IntentOperation oper : intentOperations.operations()) {
517 + IntentId intentId;
518 + IntentKey intentKey;
519 + switch (oper.type()) {
520 + case SUBMIT:
521 + intentKey = new IntentKey(oper.intent());
522 + submitIntents.add(intentKey);
523 + break;
524 + case WITHDRAW:
525 + intentId = oper.intentId();
526 + withdrawIntentIds.add(intentId);
527 + break;
528 + case REPLACE:
529 + intentKey = new IntentKey(oper.intent());
530 + replaceIntents.add(intentKey);
531 + break;
532 + case UPDATE:
533 + intentKey = new IntentKey(oper.intent());
534 + updateIntents.add(intentKey);
535 + break;
536 + default:
537 + break;
538 + }
539 + }
540 + }
541 + }
542 +
543 + /**
544 + * Matcher method to set an expected intent to match against (ignoring the
545 + * the intent ID).
546 + *
547 + * @param intent the expected intent
548 + * @return something of type IntentOperations
549 + */
550 + private static IntentOperations eqExceptId(
551 + IntentOperations intentOperations) {
552 + reportMatcher(new IdAgnosticIntentOperationsMatcher(intentOperations));
553 + return intentOperations;
554 + }
413 } 555 }
......
...@@ -20,6 +20,8 @@ import org.easymock.IArgumentMatcher; ...@@ -20,6 +20,8 @@ import org.easymock.IArgumentMatcher;
20 import org.junit.Before; 20 import org.junit.Before;
21 import org.junit.Ignore; 21 import org.junit.Ignore;
22 import org.junit.Test; 22 import org.junit.Test;
23 +import org.onlab.junit.TestUtils;
24 +import org.onlab.junit.TestUtils.TestUtilsException;
23 import org.onlab.onos.core.ApplicationId; 25 import org.onlab.onos.core.ApplicationId;
24 import org.onlab.onos.net.ConnectPoint; 26 import org.onlab.onos.net.ConnectPoint;
25 import org.onlab.onos.net.DeviceId; 27 import org.onlab.onos.net.DeviceId;
...@@ -70,9 +72,10 @@ public class PeerConnectivityManagerTest { ...@@ -70,9 +72,10 @@ public class PeerConnectivityManagerTest {
70 }; 72 };
71 73
72 private PeerConnectivityManager peerConnectivityManager; 74 private PeerConnectivityManager peerConnectivityManager;
73 - private IntentService intentService; 75 + private IntentSynchronizer intentSynchronizer;
74 private SdnIpConfigService configInfoService; 76 private SdnIpConfigService configInfoService;
75 private InterfaceService interfaceService; 77 private InterfaceService interfaceService;
78 + private IntentService intentService;
76 79
77 private Map<String, BgpSpeaker> bgpSpeakers; 80 private Map<String, BgpSpeaker> bgpSpeakers;
78 private Map<String, Interface> interfaces; 81 private Map<String, Interface> interfaces;
...@@ -525,8 +528,10 @@ public class PeerConnectivityManagerTest { ...@@ -525,8 +528,10 @@ public class PeerConnectivityManagerTest {
525 528
526 /** 529 /**
527 * Initializes peer connectivity testing environment. 530 * Initializes peer connectivity testing environment.
531 + *
532 + * @throws TestUtilsException if exceptions when using TestUtils
528 */ 533 */
529 - private void initPeerConnectivity() { 534 + private void initPeerConnectivity() throws TestUtilsException {
530 535
531 configInfoService = createMock(SdnIpConfigService.class); 536 configInfoService = createMock(SdnIpConfigService.class);
532 expect(configInfoService.getBgpPeers()).andReturn(peers).anyTimes(); 537 expect(configInfoService.getBgpPeers()).andReturn(peers).anyTimes();
...@@ -536,8 +541,13 @@ public class PeerConnectivityManagerTest { ...@@ -536,8 +541,13 @@ public class PeerConnectivityManagerTest {
536 intentService = createMock(IntentService.class); 541 intentService = createMock(IntentService.class);
537 replay(intentService); 542 replay(intentService);
538 543
539 - peerConnectivityManager = new PeerConnectivityManager(APPID, configInfoService, 544 + intentSynchronizer = new IntentSynchronizer(APPID, intentService);
540 - interfaceService, intentService); 545 + intentSynchronizer.leaderChanged(true);
546 + TestUtils.setField(intentSynchronizer, "isActivatedLeader", true);
547 +
548 + peerConnectivityManager =
549 + new PeerConnectivityManager(APPID, intentSynchronizer,
550 + configInfoService, interfaceService);
541 } 551 }
542 552
543 /* 553 /*
......
...@@ -115,8 +115,8 @@ public class RouterTest { ...@@ -115,8 +115,8 @@ public class RouterTest {
115 intentService = createMock(IntentService.class); 115 intentService = createMock(IntentService.class);
116 116
117 intentSynchronizer = new IntentSynchronizer(APPID, intentService); 117 intentSynchronizer = new IntentSynchronizer(APPID, intentService);
118 - router = new Router(APPID, intentSynchronizer, 118 + router = new Router(APPID, intentSynchronizer, sdnIpConfigService,
119 - hostService, sdnIpConfigService, interfaceService); 119 + interfaceService, hostService);
120 } 120 }
121 121
122 /** 122 /**
...@@ -267,8 +267,8 @@ public class RouterTest { ...@@ -267,8 +267,8 @@ public class RouterTest {
267 // Verify 267 // Verify
268 assertEquals(router.getRoutes().size(), 1); 268 assertEquals(router.getRoutes().size(), 1);
269 assertTrue(router.getRoutes().contains(routeEntry)); 269 assertTrue(router.getRoutes().contains(routeEntry));
270 - assertEquals(intentSynchronizer.getPushedRouteIntents().size(), 1); 270 + assertEquals(intentSynchronizer.getRouteIntents().size(), 1);
271 - assertEquals(intentSynchronizer.getPushedRouteIntents().iterator().next(), 271 + assertEquals(intentSynchronizer.getRouteIntents().iterator().next(),
272 intent); 272 intent);
273 verify(intentService); 273 verify(intentService);
274 } 274 }
...@@ -347,8 +347,8 @@ public class RouterTest { ...@@ -347,8 +347,8 @@ public class RouterTest {
347 // Verify 347 // Verify
348 assertEquals(router.getRoutes().size(), 1); 348 assertEquals(router.getRoutes().size(), 1);
349 assertTrue(router.getRoutes().contains(routeEntryUpdate)); 349 assertTrue(router.getRoutes().contains(routeEntryUpdate));
350 - assertEquals(intentSynchronizer.getPushedRouteIntents().size(), 1); 350 + assertEquals(intentSynchronizer.getRouteIntents().size(), 1);
351 - assertEquals(intentSynchronizer.getPushedRouteIntents().iterator().next(), 351 + assertEquals(intentSynchronizer.getRouteIntents().iterator().next(),
352 intentNew); 352 intentNew);
353 verify(intentService); 353 verify(intentService);
354 } 354 }
...@@ -397,7 +397,7 @@ public class RouterTest { ...@@ -397,7 +397,7 @@ public class RouterTest {
397 397
398 // Verify 398 // Verify
399 assertEquals(router.getRoutes().size(), 0); 399 assertEquals(router.getRoutes().size(), 0);
400 - assertEquals(intentSynchronizer.getPushedRouteIntents().size(), 0); 400 + assertEquals(intentSynchronizer.getRouteIntents().size(), 0);
401 verify(intentService); 401 verify(intentService);
402 } 402 }
403 403
...@@ -425,7 +425,7 @@ public class RouterTest { ...@@ -425,7 +425,7 @@ public class RouterTest {
425 // Verify 425 // Verify
426 assertEquals(router.getRoutes().size(), 1); 426 assertEquals(router.getRoutes().size(), 1);
427 assertTrue(router.getRoutes().contains(routeEntry)); 427 assertTrue(router.getRoutes().contains(routeEntry));
428 - assertEquals(intentSynchronizer.getPushedRouteIntents().size(), 0); 428 + assertEquals(intentSynchronizer.getRouteIntents().size(), 0);
429 verify(intentService); 429 verify(intentService);
430 } 430 }
431 } 431 }
......
...@@ -117,7 +117,7 @@ public class RouterTestWithAsyncArp { ...@@ -117,7 +117,7 @@ public class RouterTestWithAsyncArp {
117 117
118 intentSynchronizer = new IntentSynchronizer(APPID, intentService); 118 intentSynchronizer = new IntentSynchronizer(APPID, intentService);
119 router = new Router(APPID, intentSynchronizer, 119 router = new Router(APPID, intentSynchronizer,
120 - hostService, sdnIpConfigService, interfaceService); 120 + sdnIpConfigService, interfaceService, hostService);
121 internalHostListener = router.new InternalHostListener(); 121 internalHostListener = router.new InternalHostListener();
122 } 122 }
123 123
...@@ -229,8 +229,8 @@ public class RouterTestWithAsyncArp { ...@@ -229,8 +229,8 @@ public class RouterTestWithAsyncArp {
229 // Verify 229 // Verify
230 assertEquals(router.getRoutes().size(), 1); 230 assertEquals(router.getRoutes().size(), 1);
231 assertTrue(router.getRoutes().contains(routeEntry)); 231 assertTrue(router.getRoutes().contains(routeEntry));
232 - assertEquals(intentSynchronizer.getPushedRouteIntents().size(), 1); 232 + assertEquals(intentSynchronizer.getRouteIntents().size(), 1);
233 - assertEquals(intentSynchronizer.getPushedRouteIntents().iterator().next(), 233 + assertEquals(intentSynchronizer.getRouteIntents().iterator().next(),
234 intent); 234 intent);
235 verify(intentService); 235 verify(intentService);
236 verify(hostService); 236 verify(hostService);
...@@ -254,9 +254,9 @@ public class RouterTestWithAsyncArp { ...@@ -254,9 +254,9 @@ public class RouterTestWithAsyncArp {
254 MultiPointToSinglePointIntent intent = staticIntentBuilder(); 254 MultiPointToSinglePointIntent intent = staticIntentBuilder();
255 255
256 // Set up the bgpRoutes field of Router class with existing route, and 256 // Set up the bgpRoutes field of Router class with existing route, and
257 - // pushedRouteIntents field with the corresponding existing intent 257 + // routeIntents field with the corresponding existing intent
258 setBgpRoutesField(routeEntry); 258 setBgpRoutesField(routeEntry);
259 - setPushedRouteIntentsField(routeEntry, intent); 259 + setRouteIntentsField(routeEntry, intent);
260 260
261 // Start to construct a new route entry and new intent 261 // Start to construct a new route entry and new intent
262 RouteEntry routeEntryUpdate = new RouteEntry( 262 RouteEntry routeEntryUpdate = new RouteEntry(
...@@ -312,8 +312,8 @@ public class RouterTestWithAsyncArp { ...@@ -312,8 +312,8 @@ public class RouterTestWithAsyncArp {
312 // Verify 312 // Verify
313 assertEquals(router.getRoutes().size(), 1); 313 assertEquals(router.getRoutes().size(), 1);
314 assertTrue(router.getRoutes().contains(routeEntryUpdate)); 314 assertTrue(router.getRoutes().contains(routeEntryUpdate));
315 - assertEquals(intentSynchronizer.getPushedRouteIntents().size(), 1); 315 + assertEquals(intentSynchronizer.getRouteIntents().size(), 1);
316 - assertEquals(intentSynchronizer.getPushedRouteIntents().iterator().next(), 316 + assertEquals(intentSynchronizer.getRouteIntents().iterator().next(),
317 intentNew); 317 intentNew);
318 verify(intentService); 318 verify(intentService);
319 verify(hostService); 319 verify(hostService);
...@@ -334,9 +334,9 @@ public class RouterTestWithAsyncArp { ...@@ -334,9 +334,9 @@ public class RouterTestWithAsyncArp {
334 MultiPointToSinglePointIntent intent = staticIntentBuilder(); 334 MultiPointToSinglePointIntent intent = staticIntentBuilder();
335 335
336 // Set up the bgpRoutes field of Router class with existing route, and 336 // Set up the bgpRoutes field of Router class with existing route, and
337 - // pushedRouteIntents field with the corresponding existing intent 337 + // routeIntents field with the corresponding existing intent
338 setBgpRoutesField(routeEntry); 338 setBgpRoutesField(routeEntry);
339 - setPushedRouteIntentsField(routeEntry, intent); 339 + setRouteIntentsField(routeEntry, intent);
340 340
341 // Set up expectation 341 // Set up expectation
342 reset(intentService); 342 reset(intentService);
...@@ -350,7 +350,7 @@ public class RouterTestWithAsyncArp { ...@@ -350,7 +350,7 @@ public class RouterTestWithAsyncArp {
350 350
351 // Verify 351 // Verify
352 assertEquals(router.getRoutes().size(), 0); 352 assertEquals(router.getRoutes().size(), 0);
353 - assertEquals(intentSynchronizer.getPushedRouteIntents().size(), 0); 353 + assertEquals(intentSynchronizer.getRouteIntents().size(), 0);
354 verify(intentService); 354 verify(intentService);
355 } 355 }
356 356
...@@ -397,17 +397,17 @@ public class RouterTestWithAsyncArp { ...@@ -397,17 +397,17 @@ public class RouterTestWithAsyncArp {
397 } 397 }
398 398
399 /** 399 /**
400 - * Sets pushedRouteIntentsField in Router class. 400 + * Sets routeIntentsField in IntentSynchronizer class.
401 * 401 *
402 * @throws TestUtilsException 402 * @throws TestUtilsException
403 */ 403 */
404 - private void setPushedRouteIntentsField(RouteEntry routeEntry, 404 + private void setRouteIntentsField(RouteEntry routeEntry,
405 MultiPointToSinglePointIntent intent) 405 MultiPointToSinglePointIntent intent)
406 throws TestUtilsException { 406 throws TestUtilsException {
407 407
408 ConcurrentHashMap<Ip4Prefix, MultiPointToSinglePointIntent> 408 ConcurrentHashMap<Ip4Prefix, MultiPointToSinglePointIntent>
409 - pushedRouteIntents = new ConcurrentHashMap<>(); 409 + routeIntents = new ConcurrentHashMap<>();
410 - pushedRouteIntents.put(routeEntry.prefix(), intent); 410 + routeIntents.put(routeEntry.prefix(), intent);
411 - TestUtils.setField(router, "pushedRouteIntents", pushedRouteIntents); 411 + TestUtils.setField(intentSynchronizer, "routeIntents", routeIntents);
412 } 412 }
413 } 413 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -113,8 +113,8 @@ public class SdnIpTest { ...@@ -113,8 +113,8 @@ public class SdnIpTest {
113 random = new Random(); 113 random = new Random();
114 114
115 intentSynchronizer = new IntentSynchronizer(APPID, intentService); 115 intentSynchronizer = new IntentSynchronizer(APPID, intentService);
116 - router = new Router(APPID, intentSynchronizer, hostService, 116 + router = new Router(APPID, intentSynchronizer, sdnIpConfigService,
117 - sdnIpConfigService, interfaceService); 117 + interfaceService, hostService);
118 } 118 }
119 119
120 /** 120 /**
...@@ -241,7 +241,7 @@ public class SdnIpTest { ...@@ -241,7 +241,7 @@ public class SdnIpTest {
241 latch.await(5000, TimeUnit.MILLISECONDS); 241 latch.await(5000, TimeUnit.MILLISECONDS);
242 242
243 assertEquals(router.getRoutes().size(), numRoutes); 243 assertEquals(router.getRoutes().size(), numRoutes);
244 - assertEquals(intentSynchronizer.getPushedRouteIntents().size(), 244 + assertEquals(intentSynchronizer.getRouteIntents().size(),
245 numRoutes); 245 numRoutes);
246 246
247 verify(intentService); 247 verify(intentService);
...@@ -317,7 +317,7 @@ public class SdnIpTest { ...@@ -317,7 +317,7 @@ public class SdnIpTest {
317 deleteCount.await(5000, TimeUnit.MILLISECONDS); 317 deleteCount.await(5000, TimeUnit.MILLISECONDS);
318 318
319 assertEquals(0, router.getRoutes().size()); 319 assertEquals(0, router.getRoutes().size());
320 - assertEquals(0, intentSynchronizer.getPushedRouteIntents().size()); 320 + assertEquals(0, intentSynchronizer.getRouteIntents().size());
321 verify(intentService); 321 verify(intentService);
322 } 322 }
323 323
......
...@@ -139,6 +139,12 @@ ...@@ -139,6 +139,12 @@
139 </dependency> 139 </dependency>
140 140
141 <dependency> 141 <dependency>
142 + <groupId>org.apache.commons</groupId>
143 + <artifactId>commons-collections4</artifactId>
144 + <version>4.0</version>
145 + </dependency>
146 +
147 + <dependency>
142 <groupId>org.codehaus.jackson</groupId> 148 <groupId>org.codehaus.jackson</groupId>
143 <artifactId>jackson-core-asl</artifactId> 149 <artifactId>jackson-core-asl</artifactId>
144 <version>1.9.13</version> 150 <version>1.9.13</version>
......