blob: 332ff0b82253896f9a74de2327a3f6b8aec12ea7 [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 //
111 log.debug("Withdrawing all SDN-IP Intents...");
112 IntentOperations.Builder builder = IntentOperations.builder();
113 for (Intent intent : intentService.getIntents()) {
114 // Skip the intents from other applications
115 if (!intent.appId().equals(appId)) {
116 continue;
117 }
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());
128 }
129 intentService.execute(builder.build());
130 leaderChanged(false);
131
132 peerIntents.clear();
133 routeIntents.clear();
134 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800135 }
136
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800137 public void leaderChanged(boolean isLeader) {
138 log.debug("Leader changed: {}", isLeader);
139
140 if (!isLeader) {
141 this.isElectedLeader = false;
142 this.isActivatedLeader = false;
143 return; // Nothing to do
144 }
145 this.isActivatedLeader = false;
146 this.isElectedLeader = true;
147
148 //
149 // Tell the Intents Synchronizer thread to start the synchronization
150 //
151 intentsSynchronizerSemaphore.release();
152 }
153
154 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800155 * Gets the route intents.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800156 *
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800157 * @return the route intents
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800158 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800159 public Collection<MultiPointToSinglePointIntent> getRouteIntents() {
160 List<MultiPointToSinglePointIntent> result = new LinkedList<>();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800161
162 for (Map.Entry<Ip4Prefix, MultiPointToSinglePointIntent> entry :
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800163 routeIntents.entrySet()) {
164 result.add(entry.getValue());
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800165 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800166 return result;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800167 }
168
169 /**
170 * Thread for Intent Synchronization.
171 */
172 private void doIntentSynchronizationThread() {
173 boolean interrupted = false;
174 try {
175 while (!interrupted) {
176 try {
177 intentsSynchronizerSemaphore.acquire();
178 //
179 // Drain all permits, because a single synchronization is
180 // sufficient.
181 //
182 intentsSynchronizerSemaphore.drainPermits();
183 } catch (InterruptedException e) {
184 log.debug("Interrupted while waiting to become " +
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800185 "Intent Synchronization leader");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800186 interrupted = true;
187 break;
188 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800189 synchronizeIntents();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800190 }
191 } finally {
192 if (interrupted) {
193 Thread.currentThread().interrupt();
194 }
195 }
196 }
197
198 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800199 * 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...");
Pavlin Radoslavovdde22ae2014-11-24 11:47:17 -0800213 IntentOperations.Builder builder = IntentOperations.builder();
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800214 for (Intent intent : intents) {
Pavlin Radoslavovdde22ae2014-11-24 11:47:17 -0800215 builder.addSubmitOperation(intent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800216 }
Pavlin Radoslavovdde22ae2014-11-24 11:47:17 -0800217 intentService.execute(builder.build());
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800218 }
219 }
220 }
221
222 /**
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800223 * Submits a multi-point-to-single-point intent.
224 *
225 * @param prefix the IPv4 matching prefix for the intent to submit
226 * @param intent the intent to submit
227 */
228 void submitRouteIntent(Ip4Prefix prefix,
229 MultiPointToSinglePointIntent intent) {
230 synchronized (this) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800231 MultiPointToSinglePointIntent oldIntent =
232 routeIntents.put(prefix, intent);
233
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800234 if (isElectedLeader && isActivatedLeader) {
235 log.debug("Intent installation: adding Intent for prefix: {}",
236 prefix);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800237 if (oldIntent != null) {
238 //
239 // TODO: Short-term solution to explicitly withdraw
240 // instead of using "replace" operation.
241 //
242 intentService.withdraw(oldIntent);
243 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800244 intentService.submit(intent);
245 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800246 }
247 }
248
249 /**
250 * Withdraws a multi-point-to-single-point intent.
251 *
252 * @param prefix the IPv4 matching prefix for the intent to withdraw.
253 */
254 void withdrawRouteIntent(Ip4Prefix prefix) {
255 synchronized (this) {
256 MultiPointToSinglePointIntent intent =
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800257 routeIntents.remove(prefix);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800258
259 if (intent == null) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800260 log.debug("There is no Intent in routeIntents to " +
261 "delete for prefix: {}", prefix);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800262 return;
263 }
264
265 if (isElectedLeader && isActivatedLeader) {
266 log.debug("Intent installation: deleting Intent for prefix: {}",
267 prefix);
268 intentService.withdraw(intent);
269 }
270 }
271 }
272
273 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800274 * Synchronize the in-memory Intents with the Intents in the Intent
275 * framework.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800276 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800277 void synchronizeIntents() {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800278 synchronized (this) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800279
280 Map<IntentKey, Intent> localIntents = new HashMap<>();
281 Map<IntentKey, Intent> fetchedIntents = new HashMap<>();
282 Collection<Intent> storeInMemoryIntents = new LinkedList<>();
283 Collection<Intent> addIntents = new LinkedList<>();
284 Collection<Intent> deleteIntents = new LinkedList<>();
285
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800286 if (!isElectedLeader) {
287 return; // Nothing to do: not the leader anymore
288 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800289 log.debug("Syncing SDN-IP Intents...");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800290
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800291 // Prepare the local intents
292 for (Intent intent : routeIntents.values()) {
293 localIntents.put(new IntentKey(intent), intent);
294 }
295 for (Intent intent : peerIntents.values()) {
296 localIntents.put(new IntentKey(intent), intent);
297 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800298
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800299 // Fetch all intents for this application
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800300 for (Intent intent : intentService.getIntents()) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800301 if (!intent.appId().equals(appId)) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800302 continue;
303 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800304 fetchedIntents.put(new IntentKey(intent), intent);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800305 }
306
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800307 computeIntentsDelta(localIntents, fetchedIntents,
308 storeInMemoryIntents, addIntents,
309 deleteIntents);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800310
311 //
312 // Perform the actions:
313 // 1. Store in memory fetched intents that are same. Can be done
314 // even if we are not the leader anymore
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800315 // 2. Delete intents: check if the leader before the operation
316 // 3. Add intents: check if the leader before the operation
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800317 //
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800318 for (Intent intent : storeInMemoryIntents) {
319 // Store the intent in memory based on its type
320 if (intent instanceof MultiPointToSinglePointIntent) {
321 MultiPointToSinglePointIntent mp2pIntent =
322 (MultiPointToSinglePointIntent) intent;
323 // Find the IP prefix
324 Criterion c =
325 mp2pIntent.selector().getCriterion(Criterion.Type.IPV4_DST);
326 if (c != null && c instanceof IPCriterion) {
327 IPCriterion ipCriterion = (IPCriterion) c;
328 Ip4Prefix ip4Prefix = ipCriterion.ip().getIp4Prefix();
329 if (ip4Prefix == null) {
330 // TODO: For now we support only IPv4
331 continue;
332 }
333 log.debug("Intent synchronization: updating " +
334 "in-memory Route Intent for prefix {}",
335 ip4Prefix);
336 routeIntents.put(ip4Prefix, mp2pIntent);
337 } else {
338 log.warn("No IPV4_DST criterion found for Intent {}",
339 mp2pIntent.id());
340 }
341 continue;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800342 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800343 if (intent instanceof PointToPointIntent) {
344 PointToPointIntent p2pIntent = (PointToPointIntent) intent;
345 log.debug("Intent synchronization: updating " +
346 "in-memory Peer Intent {}", p2pIntent);
347 peerIntents.put(new IntentKey(intent), p2pIntent);
348 continue;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800349 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800350 }
351
352 // Withdraw Intents
353 IntentOperations.Builder builder = IntentOperations.builder();
354 for (Intent intent : deleteIntents) {
355 builder.addWithdrawOperation(intent.id());
356 log.debug("Intent synchronization: deleting Intent {}",
357 intent);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800358 }
359 if (!isElectedLeader) {
360 isActivatedLeader = false;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800361 return;
362 }
363 intentService.execute(builder.build());
364
365 // Add Intents
366 builder = IntentOperations.builder();
367 for (Intent intent : addIntents) {
368 builder.addSubmitOperation(intent);
369 log.debug("Intent synchronization: adding Intent {}", intent);
370 }
371 if (!isElectedLeader) {
372 isActivatedLeader = false;
373 return;
374 }
375 intentService.execute(builder.build());
376
377 if (isElectedLeader) {
378 isActivatedLeader = true; // Allow push of Intents
379 } else {
380 isActivatedLeader = false;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800381 }
382 log.debug("Syncing SDN-IP routes completed.");
383 }
384 }
385
386 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800387 * Computes the delta in two sets of Intents: local in-memory Intents,
388 * and intents fetched from the Intent framework.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800389 *
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800390 * @param localIntents the local in-memory Intents
391 * @param fetchedIntents the Intents fetched from the Intent framework
392 * @param storeInMemoryIntents the Intents that should be stored in memory.
393 * Note: This Collection must be allocated by the caller, and it will
394 * be populated by this method.
395 * @param addIntents the Intents that should be added to the Intent
396 * framework. Note: This Collection must be allocated by the caller, and
397 * it will be populated by this method.
398 * @param deleteIntents the Intents that should be deleted from the Intent
399 * framework. Note: This Collection must be allocated by the caller, and
400 * it will be populated by this method.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800401 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800402 private void computeIntentsDelta(
403 final Map<IntentKey, Intent> localIntents,
404 final Map<IntentKey, Intent> fetchedIntents,
405 Collection<Intent> storeInMemoryIntents,
406 Collection<Intent> addIntents,
407 Collection<Intent> deleteIntents) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800408
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800409 //
410 // Compute the deltas between the LOCAL in-memory Intents and the
411 // FETCHED Intents:
412 // - If an Intent is in both the LOCAL and FETCHED sets:
413 // If the FETCHED Intent is WITHDRAWING or WITHDRAWN, then
414 // the LOCAL Intent should be added/installed; otherwise the
415 // FETCHED intent should be stored in the local memory
416 // (i.e., override the LOCAL Intent) to preserve the original
417 // Intent ID.
418 // - if a LOCAL Intent is not in the FETCHED set, then the LOCAL
419 // Intent should be added/installed.
420 // - If a FETCHED Intent is not in the LOCAL set, then the FETCHED
421 // Intent should be deleted/withdrawn.
422 //
423 for (Map.Entry<IntentKey, Intent> entry : localIntents.entrySet()) {
424 IntentKey intentKey = entry.getKey();
425 Intent localIntent = entry.getValue();
426 Intent fetchedIntent = fetchedIntents.get(intentKey);
427
428 if (fetchedIntent == null) {
429 //
430 // No FETCHED Intent found: push the LOCAL Intent.
431 //
432 addIntents.add(localIntent);
433 continue;
434 }
435
436 IntentState state =
437 intentService.getIntentState(fetchedIntent.id());
438 if (state == IntentState.WITHDRAWING ||
439 state == IntentState.WITHDRAWN) {
440 // The intent has been withdrawn but according to our route
441 // table it should be installed. We'll reinstall it.
442 addIntents.add(localIntent);
443 continue;
444 }
445 storeInMemoryIntents.add(fetchedIntent);
446 }
447
448 for (Map.Entry<IntentKey, Intent> entry : fetchedIntents.entrySet()) {
449 IntentKey intentKey = entry.getKey();
450 Intent fetchedIntent = entry.getValue();
451 Intent localIntent = localIntents.get(intentKey);
452
453 if (localIntent != null) {
454 continue;
455 }
456
457 IntentState state =
458 intentService.getIntentState(fetchedIntent.id());
459 if (state == IntentState.WITHDRAWING ||
460 state == IntentState.WITHDRAWN) {
461 // Nothing to do. The intent has been already withdrawn.
462 continue;
463 }
464 //
465 // No LOCAL Intent found: delete/withdraw the FETCHED Intent.
466 //
467 deleteIntents.add(fetchedIntent);
468 }
469 }
470
471 /**
472 * Helper class that can be used to compute the key for an Intent by
473 * by excluding the Intent ID.
474 */
475 static final class IntentKey {
476 private final Intent intent;
477
478 /**
479 * Constructor.
480 *
481 * @param intent the intent to use
482 */
483 IntentKey(Intent intent) {
484 checkArgument((intent instanceof MultiPointToSinglePointIntent) ||
485 (intent instanceof PointToPointIntent),
486 "Intent type not recognized", intent);
487 this.intent = intent;
488 }
489
490 /**
491 * Compares two Multi-Point to Single-Point Intents whether they
492 * represent same logical intention.
493 *
494 * @param intent1 the first Intent to compare
495 * @param intent2 the second Intent to compare
496 * @return true if both Intents represent same logical intention,
497 * otherwise false
498 */
499 static boolean equalIntents(MultiPointToSinglePointIntent intent1,
500 MultiPointToSinglePointIntent intent2) {
501 return Objects.equals(intent1.appId(), intent2.appId()) &&
502 Objects.equals(intent1.selector(), intent2.selector()) &&
503 Objects.equals(intent1.treatment(), intent2.treatment()) &&
504 Objects.equals(intent1.ingressPoints(), intent2.ingressPoints()) &&
505 Objects.equals(intent1.egressPoint(), intent2.egressPoint());
506 }
507
508 /**
509 * Compares two Point-to-Point Intents whether they represent
510 * same logical intention.
511 *
512 * @param intent1 the first Intent to compare
513 * @param intent2 the second Intent to compare
514 * @return true if both Intents represent same logical intention,
515 * otherwise false
516 */
517 static boolean equalIntents(PointToPointIntent intent1,
518 PointToPointIntent intent2) {
519 return Objects.equals(intent1.appId(), intent2.appId()) &&
520 Objects.equals(intent1.selector(), intent2.selector()) &&
521 Objects.equals(intent1.treatment(), intent2.treatment()) &&
522 Objects.equals(intent1.ingressPoint(), intent2.ingressPoint()) &&
523 Objects.equals(intent1.egressPoint(), intent2.egressPoint());
524 }
525
526 @Override
527 public int hashCode() {
528 if (intent instanceof PointToPointIntent) {
529 PointToPointIntent p2pIntent = (PointToPointIntent) intent;
530 return Objects.hash(p2pIntent.appId(),
531 p2pIntent.resources(),
532 p2pIntent.selector(),
533 p2pIntent.treatment(),
534 p2pIntent.constraints(),
535 p2pIntent.ingressPoint(),
536 p2pIntent.egressPoint());
537 }
538 if (intent instanceof MultiPointToSinglePointIntent) {
539 MultiPointToSinglePointIntent m2pIntent =
540 (MultiPointToSinglePointIntent) intent;
541 return Objects.hash(m2pIntent.appId(),
542 m2pIntent.resources(),
543 m2pIntent.selector(),
544 m2pIntent.treatment(),
545 m2pIntent.constraints(),
546 m2pIntent.ingressPoints(),
547 m2pIntent.egressPoint());
548 }
549 checkArgument(false, "Intent type not recognized", intent);
550 return 0;
551 }
552
553 @Override
554 public boolean equals(Object obj) {
555 if (this == obj) {
556 return true;
557 }
558 if ((obj == null) || (!(obj instanceof IntentKey))) {
559 return false;
560 }
561 IntentKey other = (IntentKey) obj;
562
563 if (this.intent instanceof PointToPointIntent) {
564 if (!(other.intent instanceof PointToPointIntent)) {
565 return false;
566 }
567 return equalIntents((PointToPointIntent) this.intent,
568 (PointToPointIntent) other.intent);
569 }
570 if (this.intent instanceof MultiPointToSinglePointIntent) {
571 if (!(other.intent instanceof MultiPointToSinglePointIntent)) {
572 return false;
573 }
574 return equalIntents(
575 (MultiPointToSinglePointIntent) this.intent,
576 (MultiPointToSinglePointIntent) other.intent);
577 }
578 checkArgument(false, "Intent type not recognized", intent);
579 return false;
580 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800581 }
582}