blob: e9b69400f35e8c71f1d452cdf3071dab62016726 [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 //
Pavlin Radoslavov20be3e62014-11-25 18:52:08 -0800108 // NOTE: We don't withdraw the intents during shutdown, because
109 // it creates flux in the data plane during switchover.
110 //
111
112 /*
113 //
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800114 // Build a batch operation to withdraw all intents from this
115 // application.
116 //
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800117 log.debug("SDN-IP Intent Synchronizer shutdown: " +
118 "withdrawing all intents...");
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800119 IntentOperations.Builder builder = IntentOperations.builder();
120 for (Intent intent : intentService.getIntents()) {
121 // Skip the intents from other applications
122 if (!intent.appId().equals(appId)) {
123 continue;
124 }
125
126 // Skip the intents that are already withdrawn
127 IntentState intentState =
128 intentService.getIntentState(intent.id());
129 if (intentState.equals(IntentState.WITHDRAWING) ||
130 intentState.equals(IntentState.WITHDRAWN)) {
131 continue;
132 }
133
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800134 log.debug("SDN-IP Intent Synchronizer withdrawing intent: {}",
135 intent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800136 builder.addWithdrawOperation(intent.id());
137 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800138 IntentOperations intentOperations = builder.build();
139 intentService.execute(intentOperations);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800140 leaderChanged(false);
141
142 peerIntents.clear();
143 routeIntents.clear();
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800144 log.debug("SDN-IP Intent Synchronizer shutdown completed");
Pavlin Radoslavov20be3e62014-11-25 18:52:08 -0800145 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800146 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800147 }
148
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800149 public void leaderChanged(boolean isLeader) {
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800150 log.debug("SDN-IP Leader changed: {}", isLeader);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800151
152 if (!isLeader) {
153 this.isElectedLeader = false;
154 this.isActivatedLeader = false;
155 return; // Nothing to do
156 }
157 this.isActivatedLeader = false;
158 this.isElectedLeader = true;
159
160 //
161 // Tell the Intents Synchronizer thread to start the synchronization
162 //
163 intentsSynchronizerSemaphore.release();
164 }
165
166 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800167 * Gets the route intents.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800168 *
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800169 * @return the route intents
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800170 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800171 public Collection<MultiPointToSinglePointIntent> getRouteIntents() {
172 List<MultiPointToSinglePointIntent> result = new LinkedList<>();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800173
174 for (Map.Entry<Ip4Prefix, MultiPointToSinglePointIntent> entry :
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800175 routeIntents.entrySet()) {
176 result.add(entry.getValue());
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800177 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800178 return result;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800179 }
180
181 /**
182 * Thread for Intent Synchronization.
183 */
184 private void doIntentSynchronizationThread() {
185 boolean interrupted = false;
186 try {
187 while (!interrupted) {
188 try {
189 intentsSynchronizerSemaphore.acquire();
190 //
191 // Drain all permits, because a single synchronization is
192 // sufficient.
193 //
194 intentsSynchronizerSemaphore.drainPermits();
195 } catch (InterruptedException e) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800196 interrupted = true;
197 break;
198 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800199 synchronizeIntents();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800200 }
201 } finally {
202 if (interrupted) {
203 Thread.currentThread().interrupt();
204 }
205 }
206 }
207
208 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800209 * Submits a collection of point-to-point intents.
210 *
211 * @param intents the intents to submit
212 */
213 void submitPeerIntents(Collection<PointToPointIntent> intents) {
214 synchronized (this) {
215 // Store the intents in memory
216 for (PointToPointIntent intent : intents) {
217 peerIntents.put(new IntentKey(intent), intent);
218 }
219
220 // Push the intents
221 if (isElectedLeader && isActivatedLeader) {
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800222 log.debug("SDN-IP Submitting all Peer Intents...");
Pavlin Radoslavovdde22ae2014-11-24 11:47:17 -0800223 IntentOperations.Builder builder = IntentOperations.builder();
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800224 for (Intent intent : intents) {
Pavlin Radoslavovdde22ae2014-11-24 11:47:17 -0800225 builder.addSubmitOperation(intent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800226 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800227 IntentOperations intentOperations = builder.build();
228 log.debug("SDN-IP Submitting intents: {}",
229 intentOperations.operations());
230 intentService.execute(intentOperations);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800231 }
232 }
233 }
234
235 /**
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800236 * Submits a multi-point-to-single-point intent.
237 *
238 * @param prefix the IPv4 matching prefix for the intent to submit
239 * @param intent the intent to submit
240 */
241 void submitRouteIntent(Ip4Prefix prefix,
242 MultiPointToSinglePointIntent intent) {
243 synchronized (this) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800244 MultiPointToSinglePointIntent oldIntent =
245 routeIntents.put(prefix, intent);
246
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800247 if (isElectedLeader && isActivatedLeader) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800248 if (oldIntent != null) {
249 //
250 // TODO: Short-term solution to explicitly withdraw
251 // instead of using "replace" operation.
252 //
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800253 log.debug("SDN-IP Withdrawing old intent: {}", oldIntent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800254 intentService.withdraw(oldIntent);
255 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800256 log.debug("SDN-IP Submitting intent: {}", intent);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800257 intentService.submit(intent);
258 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800259 }
260 }
261
262 /**
263 * Withdraws a multi-point-to-single-point intent.
264 *
265 * @param prefix the IPv4 matching prefix for the intent to withdraw.
266 */
267 void withdrawRouteIntent(Ip4Prefix prefix) {
268 synchronized (this) {
269 MultiPointToSinglePointIntent intent =
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800270 routeIntents.remove(prefix);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800271
272 if (intent == null) {
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800273 log.debug("SDN-IP no intent in routeIntents to delete for " +
274 "prefix: {}", prefix);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800275 return;
276 }
277
278 if (isElectedLeader && isActivatedLeader) {
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800279 log.debug("SDN-IP Withdrawing intent: {}", intent);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800280 intentService.withdraw(intent);
281 }
282 }
283 }
284
285 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800286 * Synchronize the in-memory Intents with the Intents in the Intent
287 * framework.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800288 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800289 void synchronizeIntents() {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800290 synchronized (this) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800291
292 Map<IntentKey, Intent> localIntents = new HashMap<>();
293 Map<IntentKey, Intent> fetchedIntents = new HashMap<>();
294 Collection<Intent> storeInMemoryIntents = new LinkedList<>();
295 Collection<Intent> addIntents = new LinkedList<>();
296 Collection<Intent> deleteIntents = new LinkedList<>();
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800297 IntentOperations intentOperations;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800298
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800299 if (!isElectedLeader) {
300 return; // Nothing to do: not the leader anymore
301 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800302 log.debug("SDN-IP synchronizing all intents...");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800303
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800304 // Prepare the local intents
305 for (Intent intent : routeIntents.values()) {
306 localIntents.put(new IntentKey(intent), intent);
307 }
308 for (Intent intent : peerIntents.values()) {
309 localIntents.put(new IntentKey(intent), intent);
310 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800311
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800312 // Fetch all intents for this application
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800313 for (Intent intent : intentService.getIntents()) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800314 if (!intent.appId().equals(appId)) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800315 continue;
316 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800317 fetchedIntents.put(new IntentKey(intent), intent);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800318 }
319
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800320 computeIntentsDelta(localIntents, fetchedIntents,
321 storeInMemoryIntents, addIntents,
322 deleteIntents);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800323
324 //
325 // Perform the actions:
326 // 1. Store in memory fetched intents that are same. Can be done
327 // even if we are not the leader anymore
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800328 // 2. Delete intents: check if the leader before the operation
329 // 3. Add intents: check if the leader before the operation
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800330 //
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800331 for (Intent intent : storeInMemoryIntents) {
332 // Store the intent in memory based on its type
333 if (intent instanceof MultiPointToSinglePointIntent) {
334 MultiPointToSinglePointIntent mp2pIntent =
335 (MultiPointToSinglePointIntent) intent;
336 // Find the IP prefix
337 Criterion c =
338 mp2pIntent.selector().getCriterion(Criterion.Type.IPV4_DST);
339 if (c != null && c instanceof IPCriterion) {
340 IPCriterion ipCriterion = (IPCriterion) c;
341 Ip4Prefix ip4Prefix = ipCriterion.ip().getIp4Prefix();
342 if (ip4Prefix == null) {
343 // TODO: For now we support only IPv4
344 continue;
345 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800346 log.debug("SDN-IP Intent Synchronizer: updating " +
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800347 "in-memory Route Intent for prefix {}",
348 ip4Prefix);
349 routeIntents.put(ip4Prefix, mp2pIntent);
350 } else {
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800351 log.warn("SDN-IP no IPV4_DST criterion found for Intent {}",
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800352 mp2pIntent.id());
353 }
354 continue;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800355 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800356 if (intent instanceof PointToPointIntent) {
357 PointToPointIntent p2pIntent = (PointToPointIntent) intent;
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800358 log.debug("SDN-IP Intent Synchronizer: updating " +
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800359 "in-memory Peer Intent {}", p2pIntent);
360 peerIntents.put(new IntentKey(intent), p2pIntent);
361 continue;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800362 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800363 }
364
365 // Withdraw Intents
366 IntentOperations.Builder builder = IntentOperations.builder();
367 for (Intent intent : deleteIntents) {
368 builder.addWithdrawOperation(intent.id());
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800369 log.debug("SDN-IP Intent Synchronizer: withdrawing intent: {}",
370 intent);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800371 }
372 if (!isElectedLeader) {
373 isActivatedLeader = false;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800374 return;
375 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800376 intentOperations = builder.build();
377 intentService.execute(intentOperations);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800378
379 // Add Intents
380 builder = IntentOperations.builder();
381 for (Intent intent : addIntents) {
382 builder.addSubmitOperation(intent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800383 }
384 if (!isElectedLeader) {
385 isActivatedLeader = false;
386 return;
387 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800388 intentOperations = builder.build();
389 log.debug("SDN-IP Intent Synchronizer: submitting intents: {}",
390 intentOperations);
391 intentService.execute(intentOperations);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800392
393 if (isElectedLeader) {
394 isActivatedLeader = true; // Allow push of Intents
395 } else {
396 isActivatedLeader = false;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800397 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800398 log.debug("SDN-IP intent synchronization completed");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800399 }
400 }
401
402 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800403 * Computes the delta in two sets of Intents: local in-memory Intents,
404 * and intents fetched from the Intent framework.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800405 *
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800406 * @param localIntents the local in-memory Intents
407 * @param fetchedIntents the Intents fetched from the Intent framework
408 * @param storeInMemoryIntents the Intents that should be stored in memory.
409 * Note: This Collection must be allocated by the caller, and it will
410 * be populated by this method.
411 * @param addIntents the Intents that should be added to the Intent
412 * framework. Note: This Collection must be allocated by the caller, and
413 * it will be populated by this method.
414 * @param deleteIntents the Intents that should be deleted from the Intent
415 * framework. Note: This Collection must be allocated by the caller, and
416 * it will be populated by this method.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800417 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800418 private void computeIntentsDelta(
419 final Map<IntentKey, Intent> localIntents,
420 final Map<IntentKey, Intent> fetchedIntents,
421 Collection<Intent> storeInMemoryIntents,
422 Collection<Intent> addIntents,
423 Collection<Intent> deleteIntents) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800424
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800425 //
426 // Compute the deltas between the LOCAL in-memory Intents and the
427 // FETCHED Intents:
428 // - If an Intent is in both the LOCAL and FETCHED sets:
429 // If the FETCHED Intent is WITHDRAWING or WITHDRAWN, then
430 // the LOCAL Intent should be added/installed; otherwise the
431 // FETCHED intent should be stored in the local memory
432 // (i.e., override the LOCAL Intent) to preserve the original
433 // Intent ID.
434 // - if a LOCAL Intent is not in the FETCHED set, then the LOCAL
435 // Intent should be added/installed.
436 // - If a FETCHED Intent is not in the LOCAL set, then the FETCHED
437 // Intent should be deleted/withdrawn.
438 //
439 for (Map.Entry<IntentKey, Intent> entry : localIntents.entrySet()) {
440 IntentKey intentKey = entry.getKey();
441 Intent localIntent = entry.getValue();
442 Intent fetchedIntent = fetchedIntents.get(intentKey);
443
444 if (fetchedIntent == null) {
445 //
446 // No FETCHED Intent found: push the LOCAL Intent.
447 //
448 addIntents.add(localIntent);
449 continue;
450 }
451
452 IntentState state =
453 intentService.getIntentState(fetchedIntent.id());
454 if (state == IntentState.WITHDRAWING ||
455 state == IntentState.WITHDRAWN) {
456 // The intent has been withdrawn but according to our route
457 // table it should be installed. We'll reinstall it.
458 addIntents.add(localIntent);
459 continue;
460 }
461 storeInMemoryIntents.add(fetchedIntent);
462 }
463
464 for (Map.Entry<IntentKey, Intent> entry : fetchedIntents.entrySet()) {
465 IntentKey intentKey = entry.getKey();
466 Intent fetchedIntent = entry.getValue();
467 Intent localIntent = localIntents.get(intentKey);
468
469 if (localIntent != null) {
470 continue;
471 }
472
473 IntentState state =
474 intentService.getIntentState(fetchedIntent.id());
475 if (state == IntentState.WITHDRAWING ||
476 state == IntentState.WITHDRAWN) {
477 // Nothing to do. The intent has been already withdrawn.
478 continue;
479 }
480 //
481 // No LOCAL Intent found: delete/withdraw the FETCHED Intent.
482 //
483 deleteIntents.add(fetchedIntent);
484 }
485 }
486
487 /**
488 * Helper class that can be used to compute the key for an Intent by
489 * by excluding the Intent ID.
490 */
491 static final class IntentKey {
492 private final Intent intent;
493
494 /**
495 * Constructor.
496 *
497 * @param intent the intent to use
498 */
499 IntentKey(Intent intent) {
500 checkArgument((intent instanceof MultiPointToSinglePointIntent) ||
501 (intent instanceof PointToPointIntent),
502 "Intent type not recognized", intent);
503 this.intent = intent;
504 }
505
506 /**
507 * Compares two Multi-Point to Single-Point Intents whether they
508 * represent same logical intention.
509 *
510 * @param intent1 the first Intent to compare
511 * @param intent2 the second Intent to compare
512 * @return true if both Intents represent same logical intention,
513 * otherwise false
514 */
515 static boolean equalIntents(MultiPointToSinglePointIntent intent1,
516 MultiPointToSinglePointIntent intent2) {
517 return Objects.equals(intent1.appId(), intent2.appId()) &&
518 Objects.equals(intent1.selector(), intent2.selector()) &&
519 Objects.equals(intent1.treatment(), intent2.treatment()) &&
520 Objects.equals(intent1.ingressPoints(), intent2.ingressPoints()) &&
521 Objects.equals(intent1.egressPoint(), intent2.egressPoint());
522 }
523
524 /**
525 * Compares two Point-to-Point Intents whether they represent
526 * same logical intention.
527 *
528 * @param intent1 the first Intent to compare
529 * @param intent2 the second Intent to compare
530 * @return true if both Intents represent same logical intention,
531 * otherwise false
532 */
533 static boolean equalIntents(PointToPointIntent intent1,
534 PointToPointIntent intent2) {
535 return Objects.equals(intent1.appId(), intent2.appId()) &&
536 Objects.equals(intent1.selector(), intent2.selector()) &&
537 Objects.equals(intent1.treatment(), intent2.treatment()) &&
538 Objects.equals(intent1.ingressPoint(), intent2.ingressPoint()) &&
539 Objects.equals(intent1.egressPoint(), intent2.egressPoint());
540 }
541
542 @Override
543 public int hashCode() {
544 if (intent instanceof PointToPointIntent) {
545 PointToPointIntent p2pIntent = (PointToPointIntent) intent;
546 return Objects.hash(p2pIntent.appId(),
547 p2pIntent.resources(),
548 p2pIntent.selector(),
549 p2pIntent.treatment(),
550 p2pIntent.constraints(),
551 p2pIntent.ingressPoint(),
552 p2pIntent.egressPoint());
553 }
554 if (intent instanceof MultiPointToSinglePointIntent) {
555 MultiPointToSinglePointIntent m2pIntent =
556 (MultiPointToSinglePointIntent) intent;
557 return Objects.hash(m2pIntent.appId(),
558 m2pIntent.resources(),
559 m2pIntent.selector(),
560 m2pIntent.treatment(),
561 m2pIntent.constraints(),
562 m2pIntent.ingressPoints(),
563 m2pIntent.egressPoint());
564 }
565 checkArgument(false, "Intent type not recognized", intent);
566 return 0;
567 }
568
569 @Override
570 public boolean equals(Object obj) {
571 if (this == obj) {
572 return true;
573 }
574 if ((obj == null) || (!(obj instanceof IntentKey))) {
575 return false;
576 }
577 IntentKey other = (IntentKey) obj;
578
579 if (this.intent instanceof PointToPointIntent) {
580 if (!(other.intent instanceof PointToPointIntent)) {
581 return false;
582 }
583 return equalIntents((PointToPointIntent) this.intent,
584 (PointToPointIntent) other.intent);
585 }
586 if (this.intent instanceof MultiPointToSinglePointIntent) {
587 if (!(other.intent instanceof MultiPointToSinglePointIntent)) {
588 return false;
589 }
590 return equalIntents(
591 (MultiPointToSinglePointIntent) this.intent,
592 (MultiPointToSinglePointIntent) other.intent);
593 }
594 checkArgument(false, "Intent type not recognized", intent);
595 return false;
596 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800597 }
598}