blob: ac69faa72370aee7f29a9e58e6c2d5207d306498 [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());
Pavlin Radoslavovdeb8a102014-11-26 13:31:36 -0800129 if ((intentState == null) ||
130 intentState.equals(IntentState.WITHDRAWING) ||
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800131 intentState.equals(IntentState.WITHDRAWN)) {
132 continue;
133 }
134
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800135 log.debug("SDN-IP Intent Synchronizer withdrawing intent: {}",
136 intent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800137 builder.addWithdrawOperation(intent.id());
138 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800139 IntentOperations intentOperations = builder.build();
140 intentService.execute(intentOperations);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800141 leaderChanged(false);
142
143 peerIntents.clear();
144 routeIntents.clear();
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800145 log.debug("SDN-IP Intent Synchronizer shutdown completed");
Pavlin Radoslavov20be3e62014-11-25 18:52:08 -0800146 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800147 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800148 }
149
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800150 public void leaderChanged(boolean isLeader) {
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800151 log.debug("SDN-IP Leader changed: {}", isLeader);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800152
153 if (!isLeader) {
154 this.isElectedLeader = false;
155 this.isActivatedLeader = false;
156 return; // Nothing to do
157 }
158 this.isActivatedLeader = false;
159 this.isElectedLeader = true;
160
161 //
162 // Tell the Intents Synchronizer thread to start the synchronization
163 //
164 intentsSynchronizerSemaphore.release();
165 }
166
167 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800168 * Gets the route intents.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800169 *
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800170 * @return the route intents
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800171 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800172 public Collection<MultiPointToSinglePointIntent> getRouteIntents() {
173 List<MultiPointToSinglePointIntent> result = new LinkedList<>();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800174
175 for (Map.Entry<Ip4Prefix, MultiPointToSinglePointIntent> entry :
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800176 routeIntents.entrySet()) {
177 result.add(entry.getValue());
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800178 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800179 return result;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800180 }
181
182 /**
183 * Thread for Intent Synchronization.
184 */
185 private void doIntentSynchronizationThread() {
186 boolean interrupted = false;
187 try {
188 while (!interrupted) {
189 try {
190 intentsSynchronizerSemaphore.acquire();
191 //
192 // Drain all permits, because a single synchronization is
193 // sufficient.
194 //
195 intentsSynchronizerSemaphore.drainPermits();
196 } catch (InterruptedException e) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800197 interrupted = true;
198 break;
199 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800200 synchronizeIntents();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800201 }
202 } finally {
203 if (interrupted) {
204 Thread.currentThread().interrupt();
205 }
206 }
207 }
208
209 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800210 * Submits a collection of point-to-point intents.
211 *
212 * @param intents the intents to submit
213 */
214 void submitPeerIntents(Collection<PointToPointIntent> intents) {
215 synchronized (this) {
216 // Store the intents in memory
217 for (PointToPointIntent intent : intents) {
218 peerIntents.put(new IntentKey(intent), intent);
219 }
220
221 // Push the intents
222 if (isElectedLeader && isActivatedLeader) {
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800223 log.debug("SDN-IP Submitting all Peer Intents...");
Brian O'Connor72a034c2014-11-26 18:24:23 -0800224 IntentOperations.Builder builder = IntentOperations.builder(appId);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800225 for (Intent intent : intents) {
Pavlin Radoslavovdde22ae2014-11-24 11:47:17 -0800226 builder.addSubmitOperation(intent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800227 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800228 IntentOperations intentOperations = builder.build();
229 log.debug("SDN-IP Submitting intents: {}",
230 intentOperations.operations());
231 intentService.execute(intentOperations);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800232 }
233 }
234 }
235
236 /**
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800237 * Submits a multi-point-to-single-point intent.
238 *
239 * @param prefix the IPv4 matching prefix for the intent to submit
240 * @param intent the intent to submit
241 */
242 void submitRouteIntent(Ip4Prefix prefix,
243 MultiPointToSinglePointIntent intent) {
244 synchronized (this) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800245 MultiPointToSinglePointIntent oldIntent =
246 routeIntents.put(prefix, intent);
247
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800248 if (isElectedLeader && isActivatedLeader) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800249 if (oldIntent != null) {
250 //
251 // TODO: Short-term solution to explicitly withdraw
252 // instead of using "replace" operation.
253 //
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800254 log.debug("SDN-IP Withdrawing old intent: {}", oldIntent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800255 intentService.withdraw(oldIntent);
256 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800257 log.debug("SDN-IP Submitting intent: {}", intent);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800258 intentService.submit(intent);
259 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800260 }
261 }
262
263 /**
264 * Withdraws a multi-point-to-single-point intent.
265 *
266 * @param prefix the IPv4 matching prefix for the intent to withdraw.
267 */
268 void withdrawRouteIntent(Ip4Prefix prefix) {
269 synchronized (this) {
270 MultiPointToSinglePointIntent intent =
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800271 routeIntents.remove(prefix);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800272
273 if (intent == null) {
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800274 log.debug("SDN-IP no intent in routeIntents to delete for " +
275 "prefix: {}", prefix);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800276 return;
277 }
278
279 if (isElectedLeader && isActivatedLeader) {
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800280 log.debug("SDN-IP Withdrawing intent: {}", intent);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800281 intentService.withdraw(intent);
282 }
283 }
284 }
285
286 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800287 * Synchronize the in-memory Intents with the Intents in the Intent
288 * framework.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800289 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800290 void synchronizeIntents() {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800291 synchronized (this) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800292
293 Map<IntentKey, Intent> localIntents = new HashMap<>();
294 Map<IntentKey, Intent> fetchedIntents = new HashMap<>();
295 Collection<Intent> storeInMemoryIntents = new LinkedList<>();
296 Collection<Intent> addIntents = new LinkedList<>();
297 Collection<Intent> deleteIntents = new LinkedList<>();
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800298 IntentOperations intentOperations;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800299
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800300 if (!isElectedLeader) {
301 return; // Nothing to do: not the leader anymore
302 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800303 log.debug("SDN-IP synchronizing all intents...");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800304
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800305 // Prepare the local intents
306 for (Intent intent : routeIntents.values()) {
307 localIntents.put(new IntentKey(intent), intent);
308 }
309 for (Intent intent : peerIntents.values()) {
310 localIntents.put(new IntentKey(intent), intent);
311 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800312
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800313 // Fetch all intents for this application
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800314 for (Intent intent : intentService.getIntents()) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800315 if (!intent.appId().equals(appId)) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800316 continue;
317 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800318 fetchedIntents.put(new IntentKey(intent), intent);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800319 }
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800320 if (log.isDebugEnabled()) {
321 for (Intent intent: fetchedIntents.values()) {
322 log.debug("SDN-IP Intent Synchronizer: fetched intent: {}",
323 intent);
324 }
325 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800326
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800327 computeIntentsDelta(localIntents, fetchedIntents,
328 storeInMemoryIntents, addIntents,
329 deleteIntents);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800330
331 //
332 // Perform the actions:
333 // 1. Store in memory fetched intents that are same. Can be done
334 // even if we are not the leader anymore
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800335 // 2. Delete intents: check if the leader before the operation
336 // 3. Add intents: check if the leader before the operation
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800337 //
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800338 for (Intent intent : storeInMemoryIntents) {
339 // Store the intent in memory based on its type
340 if (intent instanceof MultiPointToSinglePointIntent) {
341 MultiPointToSinglePointIntent mp2pIntent =
342 (MultiPointToSinglePointIntent) intent;
343 // Find the IP prefix
344 Criterion c =
345 mp2pIntent.selector().getCriterion(Criterion.Type.IPV4_DST);
346 if (c != null && c instanceof IPCriterion) {
347 IPCriterion ipCriterion = (IPCriterion) c;
348 Ip4Prefix ip4Prefix = ipCriterion.ip().getIp4Prefix();
349 if (ip4Prefix == null) {
350 // TODO: For now we support only IPv4
351 continue;
352 }
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 Route Intent for prefix {}",
355 ip4Prefix);
356 routeIntents.put(ip4Prefix, mp2pIntent);
357 } else {
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800358 log.warn("SDN-IP no IPV4_DST criterion found for Intent {}",
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800359 mp2pIntent.id());
360 }
361 continue;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800362 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800363 if (intent instanceof PointToPointIntent) {
364 PointToPointIntent p2pIntent = (PointToPointIntent) intent;
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800365 log.debug("SDN-IP Intent Synchronizer: updating " +
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800366 "in-memory Peer Intent {}", p2pIntent);
367 peerIntents.put(new IntentKey(intent), p2pIntent);
368 continue;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800369 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800370 }
371
372 // Withdraw Intents
Brian O'Connor72a034c2014-11-26 18:24:23 -0800373 IntentOperations.Builder builder = IntentOperations.builder(appId);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800374 for (Intent intent : deleteIntents) {
375 builder.addWithdrawOperation(intent.id());
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800376 log.debug("SDN-IP Intent Synchronizer: withdrawing intent: {}",
377 intent);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800378 }
379 if (!isElectedLeader) {
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800380 log.debug("SDN-IP Intent Synchronizer: cannot withdraw intents: " +
381 "not elected leader anymore");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800382 isActivatedLeader = false;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800383 return;
384 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800385 intentOperations = builder.build();
386 intentService.execute(intentOperations);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800387
388 // Add Intents
Brian O'Connor72a034c2014-11-26 18:24:23 -0800389 builder = IntentOperations.builder(appId);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800390 for (Intent intent : addIntents) {
391 builder.addSubmitOperation(intent);
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800392 log.debug("SDN-IP Intent Synchronizer: submitting intent: {}",
393 intent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800394 }
395 if (!isElectedLeader) {
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800396 log.debug("SDN-IP Intent Synchronizer: cannot submit intents: " +
397 "not elected leader anymore");
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800398 isActivatedLeader = false;
399 return;
400 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800401 intentOperations = builder.build();
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800402 intentService.execute(intentOperations);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800403
404 if (isElectedLeader) {
405 isActivatedLeader = true; // Allow push of Intents
406 } else {
407 isActivatedLeader = false;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800408 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800409 log.debug("SDN-IP intent synchronization completed");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800410 }
411 }
412
413 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800414 * Computes the delta in two sets of Intents: local in-memory Intents,
415 * and intents fetched from the Intent framework.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800416 *
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800417 * @param localIntents the local in-memory Intents
418 * @param fetchedIntents the Intents fetched from the Intent framework
419 * @param storeInMemoryIntents the Intents that should be stored in memory.
420 * Note: This Collection must be allocated by the caller, and it will
421 * be populated by this method.
422 * @param addIntents the Intents that should be added to the Intent
423 * framework. Note: This Collection must be allocated by the caller, and
424 * it will be populated by this method.
425 * @param deleteIntents the Intents that should be deleted from the Intent
426 * framework. Note: This Collection must be allocated by the caller, and
427 * it will be populated by this method.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800428 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800429 private void computeIntentsDelta(
430 final Map<IntentKey, Intent> localIntents,
431 final Map<IntentKey, Intent> fetchedIntents,
432 Collection<Intent> storeInMemoryIntents,
433 Collection<Intent> addIntents,
434 Collection<Intent> deleteIntents) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800435
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800436 //
437 // Compute the deltas between the LOCAL in-memory Intents and the
438 // FETCHED Intents:
439 // - If an Intent is in both the LOCAL and FETCHED sets:
440 // If the FETCHED Intent is WITHDRAWING or WITHDRAWN, then
441 // the LOCAL Intent should be added/installed; otherwise the
442 // FETCHED intent should be stored in the local memory
443 // (i.e., override the LOCAL Intent) to preserve the original
444 // Intent ID.
445 // - if a LOCAL Intent is not in the FETCHED set, then the LOCAL
446 // Intent should be added/installed.
447 // - If a FETCHED Intent is not in the LOCAL set, then the FETCHED
448 // Intent should be deleted/withdrawn.
449 //
450 for (Map.Entry<IntentKey, Intent> entry : localIntents.entrySet()) {
451 IntentKey intentKey = entry.getKey();
452 Intent localIntent = entry.getValue();
453 Intent fetchedIntent = fetchedIntents.get(intentKey);
454
455 if (fetchedIntent == null) {
456 //
457 // No FETCHED Intent found: push the LOCAL Intent.
458 //
459 addIntents.add(localIntent);
460 continue;
461 }
462
463 IntentState state =
464 intentService.getIntentState(fetchedIntent.id());
Pavlin Radoslavovdeb8a102014-11-26 13:31:36 -0800465 if (state == null ||
466 state == IntentState.WITHDRAWING ||
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800467 state == IntentState.WITHDRAWN) {
468 // The intent has been withdrawn but according to our route
469 // table it should be installed. We'll reinstall it.
470 addIntents.add(localIntent);
471 continue;
472 }
473 storeInMemoryIntents.add(fetchedIntent);
474 }
475
476 for (Map.Entry<IntentKey, Intent> entry : fetchedIntents.entrySet()) {
477 IntentKey intentKey = entry.getKey();
478 Intent fetchedIntent = entry.getValue();
479 Intent localIntent = localIntents.get(intentKey);
480
481 if (localIntent != null) {
482 continue;
483 }
484
485 IntentState state =
486 intentService.getIntentState(fetchedIntent.id());
Pavlin Radoslavovdeb8a102014-11-26 13:31:36 -0800487 if (state == null ||
488 state == IntentState.WITHDRAWING ||
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800489 state == IntentState.WITHDRAWN) {
490 // Nothing to do. The intent has been already withdrawn.
491 continue;
492 }
493 //
494 // No LOCAL Intent found: delete/withdraw the FETCHED Intent.
495 //
496 deleteIntents.add(fetchedIntent);
497 }
498 }
499
500 /**
501 * Helper class that can be used to compute the key for an Intent by
502 * by excluding the Intent ID.
503 */
504 static final class IntentKey {
505 private final Intent intent;
506
507 /**
508 * Constructor.
509 *
510 * @param intent the intent to use
511 */
512 IntentKey(Intent intent) {
513 checkArgument((intent instanceof MultiPointToSinglePointIntent) ||
514 (intent instanceof PointToPointIntent),
515 "Intent type not recognized", intent);
516 this.intent = intent;
517 }
518
519 /**
520 * Compares two Multi-Point to Single-Point Intents whether they
521 * represent 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(MultiPointToSinglePointIntent intent1,
529 MultiPointToSinglePointIntent 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.ingressPoints(), intent2.ingressPoints()) &&
534 Objects.equals(intent1.egressPoint(), intent2.egressPoint());
535 }
536
537 /**
538 * Compares two Point-to-Point Intents whether they represent
539 * same logical intention.
540 *
541 * @param intent1 the first Intent to compare
542 * @param intent2 the second Intent to compare
543 * @return true if both Intents represent same logical intention,
544 * otherwise false
545 */
546 static boolean equalIntents(PointToPointIntent intent1,
547 PointToPointIntent intent2) {
548 return Objects.equals(intent1.appId(), intent2.appId()) &&
549 Objects.equals(intent1.selector(), intent2.selector()) &&
550 Objects.equals(intent1.treatment(), intent2.treatment()) &&
551 Objects.equals(intent1.ingressPoint(), intent2.ingressPoint()) &&
552 Objects.equals(intent1.egressPoint(), intent2.egressPoint());
553 }
554
555 @Override
556 public int hashCode() {
557 if (intent instanceof PointToPointIntent) {
558 PointToPointIntent p2pIntent = (PointToPointIntent) intent;
559 return Objects.hash(p2pIntent.appId(),
560 p2pIntent.resources(),
561 p2pIntent.selector(),
562 p2pIntent.treatment(),
563 p2pIntent.constraints(),
564 p2pIntent.ingressPoint(),
565 p2pIntent.egressPoint());
566 }
567 if (intent instanceof MultiPointToSinglePointIntent) {
568 MultiPointToSinglePointIntent m2pIntent =
569 (MultiPointToSinglePointIntent) intent;
570 return Objects.hash(m2pIntent.appId(),
571 m2pIntent.resources(),
572 m2pIntent.selector(),
573 m2pIntent.treatment(),
574 m2pIntent.constraints(),
575 m2pIntent.ingressPoints(),
576 m2pIntent.egressPoint());
577 }
578 checkArgument(false, "Intent type not recognized", intent);
579 return 0;
580 }
581
582 @Override
583 public boolean equals(Object obj) {
584 if (this == obj) {
585 return true;
586 }
587 if ((obj == null) || (!(obj instanceof IntentKey))) {
588 return false;
589 }
590 IntentKey other = (IntentKey) obj;
591
592 if (this.intent instanceof PointToPointIntent) {
593 if (!(other.intent instanceof PointToPointIntent)) {
594 return false;
595 }
596 return equalIntents((PointToPointIntent) this.intent,
597 (PointToPointIntent) other.intent);
598 }
599 if (this.intent instanceof MultiPointToSinglePointIntent) {
600 if (!(other.intent instanceof MultiPointToSinglePointIntent)) {
601 return false;
602 }
603 return equalIntents(
604 (MultiPointToSinglePointIntent) this.intent,
605 (MultiPointToSinglePointIntent) other.intent);
606 }
607 checkArgument(false, "Intent type not recognized", intent);
608 return false;
609 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800610 }
611}