blob: c56746d2efbe2168cb2d696256ae1c5a40645e90 [file] [log] [blame]
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -08001/*
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 */
16package org.onlab.onos.sdnip;
17
18import java.util.Collection;
19import java.util.HashMap;
20import java.util.LinkedList;
21import java.util.List;
22import java.util.Map;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -080023import java.util.Objects;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080024import java.util.concurrent.ConcurrentHashMap;
25import java.util.concurrent.ExecutorService;
26import java.util.concurrent.Executors;
27import java.util.concurrent.Semaphore;
28
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080029import org.onlab.onos.core.ApplicationId;
30import org.onlab.onos.net.flow.criteria.Criteria.IPCriterion;
31import org.onlab.onos.net.flow.criteria.Criterion;
32import org.onlab.onos.net.intent.Intent;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -080033import org.onlab.onos.net.intent.IntentOperations;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080034import org.onlab.onos.net.intent.IntentService;
35import org.onlab.onos.net.intent.IntentState;
36import org.onlab.onos.net.intent.MultiPointToSinglePointIntent;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -080037import org.onlab.onos.net.intent.PointToPointIntent;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080038import org.onlab.packet.Ip4Prefix;
39import org.slf4j.Logger;
40import org.slf4j.LoggerFactory;
41
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080042import com.google.common.util.concurrent.ThreadFactoryBuilder;
43
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -080044import static com.google.common.base.Preconditions.checkArgument;
45
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080046public class IntentSynchronizer {
47 private static final Logger log =
48 LoggerFactory.getLogger(IntentSynchronizer.class);
49
50 private final ApplicationId appId;
51 private final IntentService intentService;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -080052 private final Map<IntentKey, PointToPointIntent> peerIntents;
53 private final Map<Ip4Prefix, MultiPointToSinglePointIntent> routeIntents;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080054
55 //
56 // State to deal with SDN-IP Leader election and pushing Intents
57 //
58 private final ExecutorService bgpIntentsSynchronizerExecutor;
59 private final Semaphore intentsSynchronizerSemaphore = new Semaphore(0);
60 private volatile boolean isElectedLeader = false;
61 private volatile boolean isActivatedLeader = false;
62
63 /**
64 * Class constructor.
65 *
66 * @param appId the Application ID
67 * @param intentService the intent service
68 */
69 IntentSynchronizer(ApplicationId appId, IntentService intentService) {
70 this.appId = appId;
71 this.intentService = intentService;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -080072 peerIntents = new ConcurrentHashMap<>();
73 routeIntents = new ConcurrentHashMap<>();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080074
75 bgpIntentsSynchronizerExecutor = Executors.newSingleThreadExecutor(
76 new ThreadFactoryBuilder()
Pavlin Radoslavov8b752442014-11-18 14:34:37 -080077 .setNameFormat("sdnip-intents-synchronizer-%d").build());
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080078 }
79
80 /**
81 * Starts the synchronizer.
82 */
83 public void start() {
84 bgpIntentsSynchronizerExecutor.execute(new Runnable() {
85 @Override
86 public void run() {
87 doIntentSynchronizationThread();
88 }
89 });
90 }
91
92 /**
93 * Stops the synchronizer.
94 */
95 public void stop() {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -080096 synchronized (this) {
97 // Stop the thread(s)
98 bgpIntentsSynchronizerExecutor.shutdownNow();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080099
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800100 //
101 // Withdraw all SDN-IP intents
102 //
103 if (!isElectedLeader) {
104 return; // Nothing to do: not the leader anymore
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800105 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800106
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800107 //
108 // Build a batch operation to withdraw all intents from this
109 // application.
110 //
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800111 log.debug("SDN-IP Intent Synchronizer shutdown: " +
112 "withdrawing all intents...");
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800113 IntentOperations.Builder builder = IntentOperations.builder();
114 for (Intent intent : intentService.getIntents()) {
115 // Skip the intents from other applications
116 if (!intent.appId().equals(appId)) {
117 continue;
118 }
119
120 // Skip the intents that are already withdrawn
121 IntentState intentState =
122 intentService.getIntentState(intent.id());
123 if (intentState.equals(IntentState.WITHDRAWING) ||
124 intentState.equals(IntentState.WITHDRAWN)) {
125 continue;
126 }
127
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800128 log.debug("SDN-IP Intent Synchronizer withdrawing intent: {}",
129 intent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800130 builder.addWithdrawOperation(intent.id());
131 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800132 IntentOperations intentOperations = builder.build();
133 intentService.execute(intentOperations);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800134 leaderChanged(false);
135
136 peerIntents.clear();
137 routeIntents.clear();
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800138 log.debug("SDN-IP Intent Synchronizer shutdown completed");
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800139 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800140 }
141
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800142 public void leaderChanged(boolean isLeader) {
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800143 log.debug("SDN-IP Leader changed: {}", isLeader);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800144
145 if (!isLeader) {
146 this.isElectedLeader = false;
147 this.isActivatedLeader = false;
148 return; // Nothing to do
149 }
150 this.isActivatedLeader = false;
151 this.isElectedLeader = true;
152
153 //
154 // Tell the Intents Synchronizer thread to start the synchronization
155 //
156 intentsSynchronizerSemaphore.release();
157 }
158
159 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800160 * Gets the route intents.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800161 *
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800162 * @return the route intents
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800163 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800164 public Collection<MultiPointToSinglePointIntent> getRouteIntents() {
165 List<MultiPointToSinglePointIntent> result = new LinkedList<>();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800166
167 for (Map.Entry<Ip4Prefix, MultiPointToSinglePointIntent> entry :
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800168 routeIntents.entrySet()) {
169 result.add(entry.getValue());
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800170 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800171 return result;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800172 }
173
174 /**
175 * Thread for Intent Synchronization.
176 */
177 private void doIntentSynchronizationThread() {
178 boolean interrupted = false;
179 try {
180 while (!interrupted) {
181 try {
182 intentsSynchronizerSemaphore.acquire();
183 //
184 // Drain all permits, because a single synchronization is
185 // sufficient.
186 //
187 intentsSynchronizerSemaphore.drainPermits();
188 } catch (InterruptedException e) {
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800189 log.debug("SDN-IP interrupted while waiting to become " +
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800190 "Intent Synchronization leader");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800191 interrupted = true;
192 break;
193 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800194 synchronizeIntents();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800195 }
196 } finally {
197 if (interrupted) {
198 Thread.currentThread().interrupt();
199 }
200 }
201 }
202
203 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800204 * Submits a collection of point-to-point intents.
205 *
206 * @param intents the intents to submit
207 */
208 void submitPeerIntents(Collection<PointToPointIntent> intents) {
209 synchronized (this) {
210 // Store the intents in memory
211 for (PointToPointIntent intent : intents) {
212 peerIntents.put(new IntentKey(intent), intent);
213 }
214
215 // Push the intents
216 if (isElectedLeader && isActivatedLeader) {
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800217 log.debug("SDN-IP Submitting all Peer Intents...");
Pavlin Radoslavovdde22ae2014-11-24 11:47:17 -0800218 IntentOperations.Builder builder = IntentOperations.builder();
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800219 for (Intent intent : intents) {
Pavlin Radoslavovdde22ae2014-11-24 11:47:17 -0800220 builder.addSubmitOperation(intent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800221 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800222 IntentOperations intentOperations = builder.build();
223 log.debug("SDN-IP Submitting intents: {}",
224 intentOperations.operations());
225 intentService.execute(intentOperations);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800226 }
227 }
228 }
229
230 /**
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800231 * Submits a multi-point-to-single-point intent.
232 *
233 * @param prefix the IPv4 matching prefix for the intent to submit
234 * @param intent the intent to submit
235 */
236 void submitRouteIntent(Ip4Prefix prefix,
237 MultiPointToSinglePointIntent intent) {
238 synchronized (this) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800239 MultiPointToSinglePointIntent oldIntent =
240 routeIntents.put(prefix, intent);
241
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800242 if (isElectedLeader && isActivatedLeader) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800243 if (oldIntent != null) {
244 //
245 // TODO: Short-term solution to explicitly withdraw
246 // instead of using "replace" operation.
247 //
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800248 log.debug("SDN-IP Withdrawing old intent: {}", oldIntent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800249 intentService.withdraw(oldIntent);
250 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800251 log.debug("SDN-IP Submitting intent: {}", intent);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800252 intentService.submit(intent);
253 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800254 }
255 }
256
257 /**
258 * Withdraws a multi-point-to-single-point intent.
259 *
260 * @param prefix the IPv4 matching prefix for the intent to withdraw.
261 */
262 void withdrawRouteIntent(Ip4Prefix prefix) {
263 synchronized (this) {
264 MultiPointToSinglePointIntent intent =
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800265 routeIntents.remove(prefix);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800266
267 if (intent == null) {
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800268 log.debug("SDN-IP no intent in routeIntents to delete for " +
269 "prefix: {}", prefix);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800270 return;
271 }
272
273 if (isElectedLeader && isActivatedLeader) {
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800274 log.debug("SDN-IP Withdrawing intent: {}", intent);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800275 intentService.withdraw(intent);
276 }
277 }
278 }
279
280 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800281 * Synchronize the in-memory Intents with the Intents in the Intent
282 * framework.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800283 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800284 void synchronizeIntents() {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800285 synchronized (this) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800286
287 Map<IntentKey, Intent> localIntents = new HashMap<>();
288 Map<IntentKey, Intent> fetchedIntents = new HashMap<>();
289 Collection<Intent> storeInMemoryIntents = new LinkedList<>();
290 Collection<Intent> addIntents = new LinkedList<>();
291 Collection<Intent> deleteIntents = new LinkedList<>();
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800292 IntentOperations intentOperations;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800293
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800294 if (!isElectedLeader) {
295 return; // Nothing to do: not the leader anymore
296 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800297 log.debug("SDN-IP synchronizing all intents...");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800298
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800299 // Prepare the local intents
300 for (Intent intent : routeIntents.values()) {
301 localIntents.put(new IntentKey(intent), intent);
302 }
303 for (Intent intent : peerIntents.values()) {
304 localIntents.put(new IntentKey(intent), intent);
305 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800306
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800307 // Fetch all intents for this application
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800308 for (Intent intent : intentService.getIntents()) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800309 if (!intent.appId().equals(appId)) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800310 continue;
311 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800312 fetchedIntents.put(new IntentKey(intent), intent);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800313 }
314
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800315 computeIntentsDelta(localIntents, fetchedIntents,
316 storeInMemoryIntents, addIntents,
317 deleteIntents);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800318
319 //
320 // Perform the actions:
321 // 1. Store in memory fetched intents that are same. Can be done
322 // even if we are not the leader anymore
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800323 // 2. Delete intents: check if the leader before the operation
324 // 3. Add intents: check if the leader before the operation
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800325 //
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800326 for (Intent intent : storeInMemoryIntents) {
327 // Store the intent in memory based on its type
328 if (intent instanceof MultiPointToSinglePointIntent) {
329 MultiPointToSinglePointIntent mp2pIntent =
330 (MultiPointToSinglePointIntent) intent;
331 // Find the IP prefix
332 Criterion c =
333 mp2pIntent.selector().getCriterion(Criterion.Type.IPV4_DST);
334 if (c != null && c instanceof IPCriterion) {
335 IPCriterion ipCriterion = (IPCriterion) c;
336 Ip4Prefix ip4Prefix = ipCriterion.ip().getIp4Prefix();
337 if (ip4Prefix == null) {
338 // TODO: For now we support only IPv4
339 continue;
340 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800341 log.debug("SDN-IP Intent Synchronizer: updating " +
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800342 "in-memory Route Intent for prefix {}",
343 ip4Prefix);
344 routeIntents.put(ip4Prefix, mp2pIntent);
345 } else {
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800346 log.warn("SDN-IP no IPV4_DST criterion found for Intent {}",
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800347 mp2pIntent.id());
348 }
349 continue;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800350 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800351 if (intent instanceof PointToPointIntent) {
352 PointToPointIntent p2pIntent = (PointToPointIntent) intent;
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800353 log.debug("SDN-IP Intent Synchronizer: updating " +
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800354 "in-memory Peer Intent {}", p2pIntent);
355 peerIntents.put(new IntentKey(intent), p2pIntent);
356 continue;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800357 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800358 }
359
360 // Withdraw Intents
361 IntentOperations.Builder builder = IntentOperations.builder();
362 for (Intent intent : deleteIntents) {
363 builder.addWithdrawOperation(intent.id());
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800364 log.debug("SDN-IP Intent Synchronizer: withdrawing intent: {}",
365 intent);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800366 }
367 if (!isElectedLeader) {
368 isActivatedLeader = false;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800369 return;
370 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800371 intentOperations = builder.build();
372 intentService.execute(intentOperations);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800373
374 // Add Intents
375 builder = IntentOperations.builder();
376 for (Intent intent : addIntents) {
377 builder.addSubmitOperation(intent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800378 }
379 if (!isElectedLeader) {
380 isActivatedLeader = false;
381 return;
382 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800383 intentOperations = builder.build();
384 log.debug("SDN-IP Intent Synchronizer: submitting intents: {}",
385 intentOperations);
386 intentService.execute(intentOperations);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800387
388 if (isElectedLeader) {
389 isActivatedLeader = true; // Allow push of Intents
390 } else {
391 isActivatedLeader = false;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800392 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800393 log.debug("SDN-IP intent synchronization completed");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800394 }
395 }
396
397 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800398 * Computes the delta in two sets of Intents: local in-memory Intents,
399 * and intents fetched from the Intent framework.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800400 *
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800401 * @param localIntents the local in-memory Intents
402 * @param fetchedIntents the Intents fetched from the Intent framework
403 * @param storeInMemoryIntents the Intents that should be stored in memory.
404 * Note: This Collection must be allocated by the caller, and it will
405 * be populated by this method.
406 * @param addIntents the Intents that should be added to the Intent
407 * framework. Note: This Collection must be allocated by the caller, and
408 * it will be populated by this method.
409 * @param deleteIntents the Intents that should be deleted from the Intent
410 * framework. Note: This Collection must be allocated by the caller, and
411 * it will be populated by this method.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800412 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800413 private void computeIntentsDelta(
414 final Map<IntentKey, Intent> localIntents,
415 final Map<IntentKey, Intent> fetchedIntents,
416 Collection<Intent> storeInMemoryIntents,
417 Collection<Intent> addIntents,
418 Collection<Intent> deleteIntents) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800419
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800420 //
421 // Compute the deltas between the LOCAL in-memory Intents and the
422 // FETCHED Intents:
423 // - If an Intent is in both the LOCAL and FETCHED sets:
424 // If the FETCHED Intent is WITHDRAWING or WITHDRAWN, then
425 // the LOCAL Intent should be added/installed; otherwise the
426 // FETCHED intent should be stored in the local memory
427 // (i.e., override the LOCAL Intent) to preserve the original
428 // Intent ID.
429 // - if a LOCAL Intent is not in the FETCHED set, then the LOCAL
430 // Intent should be added/installed.
431 // - If a FETCHED Intent is not in the LOCAL set, then the FETCHED
432 // Intent should be deleted/withdrawn.
433 //
434 for (Map.Entry<IntentKey, Intent> entry : localIntents.entrySet()) {
435 IntentKey intentKey = entry.getKey();
436 Intent localIntent = entry.getValue();
437 Intent fetchedIntent = fetchedIntents.get(intentKey);
438
439 if (fetchedIntent == null) {
440 //
441 // No FETCHED Intent found: push the LOCAL Intent.
442 //
443 addIntents.add(localIntent);
444 continue;
445 }
446
447 IntentState state =
448 intentService.getIntentState(fetchedIntent.id());
449 if (state == IntentState.WITHDRAWING ||
450 state == IntentState.WITHDRAWN) {
451 // The intent has been withdrawn but according to our route
452 // table it should be installed. We'll reinstall it.
453 addIntents.add(localIntent);
454 continue;
455 }
456 storeInMemoryIntents.add(fetchedIntent);
457 }
458
459 for (Map.Entry<IntentKey, Intent> entry : fetchedIntents.entrySet()) {
460 IntentKey intentKey = entry.getKey();
461 Intent fetchedIntent = entry.getValue();
462 Intent localIntent = localIntents.get(intentKey);
463
464 if (localIntent != null) {
465 continue;
466 }
467
468 IntentState state =
469 intentService.getIntentState(fetchedIntent.id());
470 if (state == IntentState.WITHDRAWING ||
471 state == IntentState.WITHDRAWN) {
472 // Nothing to do. The intent has been already withdrawn.
473 continue;
474 }
475 //
476 // No LOCAL Intent found: delete/withdraw the FETCHED Intent.
477 //
478 deleteIntents.add(fetchedIntent);
479 }
480 }
481
482 /**
483 * Helper class that can be used to compute the key for an Intent by
484 * by excluding the Intent ID.
485 */
486 static final class IntentKey {
487 private final Intent intent;
488
489 /**
490 * Constructor.
491 *
492 * @param intent the intent to use
493 */
494 IntentKey(Intent intent) {
495 checkArgument((intent instanceof MultiPointToSinglePointIntent) ||
496 (intent instanceof PointToPointIntent),
497 "Intent type not recognized", intent);
498 this.intent = intent;
499 }
500
501 /**
502 * Compares two Multi-Point to Single-Point Intents whether they
503 * represent same logical intention.
504 *
505 * @param intent1 the first Intent to compare
506 * @param intent2 the second Intent to compare
507 * @return true if both Intents represent same logical intention,
508 * otherwise false
509 */
510 static boolean equalIntents(MultiPointToSinglePointIntent intent1,
511 MultiPointToSinglePointIntent intent2) {
512 return Objects.equals(intent1.appId(), intent2.appId()) &&
513 Objects.equals(intent1.selector(), intent2.selector()) &&
514 Objects.equals(intent1.treatment(), intent2.treatment()) &&
515 Objects.equals(intent1.ingressPoints(), intent2.ingressPoints()) &&
516 Objects.equals(intent1.egressPoint(), intent2.egressPoint());
517 }
518
519 /**
520 * Compares two Point-to-Point Intents whether they represent
521 * same logical intention.
522 *
523 * @param intent1 the first Intent to compare
524 * @param intent2 the second Intent to compare
525 * @return true if both Intents represent same logical intention,
526 * otherwise false
527 */
528 static boolean equalIntents(PointToPointIntent intent1,
529 PointToPointIntent intent2) {
530 return Objects.equals(intent1.appId(), intent2.appId()) &&
531 Objects.equals(intent1.selector(), intent2.selector()) &&
532 Objects.equals(intent1.treatment(), intent2.treatment()) &&
533 Objects.equals(intent1.ingressPoint(), intent2.ingressPoint()) &&
534 Objects.equals(intent1.egressPoint(), intent2.egressPoint());
535 }
536
537 @Override
538 public int hashCode() {
539 if (intent instanceof PointToPointIntent) {
540 PointToPointIntent p2pIntent = (PointToPointIntent) intent;
541 return Objects.hash(p2pIntent.appId(),
542 p2pIntent.resources(),
543 p2pIntent.selector(),
544 p2pIntent.treatment(),
545 p2pIntent.constraints(),
546 p2pIntent.ingressPoint(),
547 p2pIntent.egressPoint());
548 }
549 if (intent instanceof MultiPointToSinglePointIntent) {
550 MultiPointToSinglePointIntent m2pIntent =
551 (MultiPointToSinglePointIntent) intent;
552 return Objects.hash(m2pIntent.appId(),
553 m2pIntent.resources(),
554 m2pIntent.selector(),
555 m2pIntent.treatment(),
556 m2pIntent.constraints(),
557 m2pIntent.ingressPoints(),
558 m2pIntent.egressPoint());
559 }
560 checkArgument(false, "Intent type not recognized", intent);
561 return 0;
562 }
563
564 @Override
565 public boolean equals(Object obj) {
566 if (this == obj) {
567 return true;
568 }
569 if ((obj == null) || (!(obj instanceof IntentKey))) {
570 return false;
571 }
572 IntentKey other = (IntentKey) obj;
573
574 if (this.intent instanceof PointToPointIntent) {
575 if (!(other.intent instanceof PointToPointIntent)) {
576 return false;
577 }
578 return equalIntents((PointToPointIntent) this.intent,
579 (PointToPointIntent) other.intent);
580 }
581 if (this.intent instanceof MultiPointToSinglePointIntent) {
582 if (!(other.intent instanceof MultiPointToSinglePointIntent)) {
583 return false;
584 }
585 return equalIntents(
586 (MultiPointToSinglePointIntent) this.intent,
587 (MultiPointToSinglePointIntent) other.intent);
588 }
589 checkArgument(false, "Intent type not recognized", intent);
590 return false;
591 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800592 }
593}