blob: 0a0e56995a2e0071bea284c4bf68221815916cf8 [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 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.sdnip;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080017
Jonathan Hart51372182014-12-03 21:32:34 -080018import static com.google.common.base.Preconditions.checkArgument;
19
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080020import java.util.Collection;
21import java.util.HashMap;
22import java.util.LinkedList;
23import java.util.List;
24import java.util.Map;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -080025import java.util.Objects;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080026import java.util.concurrent.ConcurrentHashMap;
27import java.util.concurrent.ExecutorService;
28import java.util.concurrent.Executors;
29import java.util.concurrent.Semaphore;
30
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -080031import org.apache.commons.lang3.tuple.Pair;
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080032import org.onlab.packet.IpPrefix;
Brian O'Connorabafb502014-12-02 22:26:20 -080033import org.onosproject.core.ApplicationId;
34import org.onosproject.net.flow.criteria.Criteria.IPCriterion;
35import org.onosproject.net.flow.criteria.Criterion;
36import org.onosproject.net.intent.Intent;
37import org.onosproject.net.intent.IntentOperations;
38import org.onosproject.net.intent.IntentService;
39import org.onosproject.net.intent.IntentState;
40import org.onosproject.net.intent.MultiPointToSinglePointIntent;
41import org.onosproject.net.intent.PointToPointIntent;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080042import org.slf4j.Logger;
43import org.slf4j.LoggerFactory;
44
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080045import com.google.common.util.concurrent.ThreadFactoryBuilder;
46
Jonathan Hart51372182014-12-03 21:32:34 -080047/**
48 * Synchronizes intents between the in-memory intent store and the
49 * IntentService.
50 */
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080051public class IntentSynchronizer {
52 private static final Logger log =
53 LoggerFactory.getLogger(IntentSynchronizer.class);
54
55 private final ApplicationId appId;
56 private final IntentService intentService;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -080057 private final Map<IntentKey, PointToPointIntent> peerIntents;
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080058 private final Map<IpPrefix, MultiPointToSinglePointIntent> routeIntents;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080059
60 //
61 // State to deal with SDN-IP Leader election and pushing Intents
62 //
63 private final ExecutorService bgpIntentsSynchronizerExecutor;
64 private final Semaphore intentsSynchronizerSemaphore = new Semaphore(0);
65 private volatile boolean isElectedLeader = false;
66 private volatile boolean isActivatedLeader = false;
67
68 /**
69 * Class constructor.
70 *
71 * @param appId the Application ID
72 * @param intentService the intent service
73 */
74 IntentSynchronizer(ApplicationId appId, IntentService intentService) {
75 this.appId = appId;
76 this.intentService = intentService;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -080077 peerIntents = new ConcurrentHashMap<>();
78 routeIntents = new ConcurrentHashMap<>();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080079
80 bgpIntentsSynchronizerExecutor = Executors.newSingleThreadExecutor(
81 new ThreadFactoryBuilder()
Pavlin Radoslavov8b752442014-11-18 14:34:37 -080082 .setNameFormat("sdnip-intents-synchronizer-%d").build());
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080083 }
84
85 /**
86 * Starts the synchronizer.
87 */
88 public void start() {
89 bgpIntentsSynchronizerExecutor.execute(new Runnable() {
90 @Override
91 public void run() {
92 doIntentSynchronizationThread();
93 }
94 });
95 }
96
97 /**
98 * Stops the synchronizer.
99 */
100 public void stop() {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800101 synchronized (this) {
102 // Stop the thread(s)
103 bgpIntentsSynchronizerExecutor.shutdownNow();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800104
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800105 //
106 // Withdraw all SDN-IP intents
107 //
108 if (!isElectedLeader) {
109 return; // Nothing to do: not the leader anymore
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800110 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800111
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800112 //
Pavlin Radoslavov20be3e62014-11-25 18:52:08 -0800113 // NOTE: We don't withdraw the intents during shutdown, because
114 // it creates flux in the data plane during switchover.
115 //
116
117 /*
118 //
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800119 // Build a batch operation to withdraw all intents from this
120 // application.
121 //
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800122 log.debug("SDN-IP Intent Synchronizer shutdown: " +
123 "withdrawing all intents...");
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800124 IntentOperations.Builder builder = IntentOperations.builder(appId);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800125 for (Intent intent : intentService.getIntents()) {
126 // Skip the intents from other applications
127 if (!intent.appId().equals(appId)) {
128 continue;
129 }
130
131 // Skip the intents that are already withdrawn
132 IntentState intentState =
133 intentService.getIntentState(intent.id());
Pavlin Radoslavovdeb8a102014-11-26 13:31:36 -0800134 if ((intentState == null) ||
135 intentState.equals(IntentState.WITHDRAWING) ||
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800136 intentState.equals(IntentState.WITHDRAWN)) {
137 continue;
138 }
139
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800140 log.trace("SDN-IP Intent Synchronizer withdrawing intent: {}",
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800141 intent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800142 builder.addWithdrawOperation(intent.id());
143 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800144 IntentOperations intentOperations = builder.build();
145 intentService.execute(intentOperations);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800146 leaderChanged(false);
147
148 peerIntents.clear();
149 routeIntents.clear();
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800150 log.debug("SDN-IP Intent Synchronizer shutdown completed");
Pavlin Radoslavov20be3e62014-11-25 18:52:08 -0800151 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800152 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800153 }
154
Jonathan Hart51372182014-12-03 21:32:34 -0800155 /**
156 * Signals the synchronizer that the SDN-IP leadership has changed.
157 *
158 * @param isLeader true if this instance is now the leader, otherwise false
159 */
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800160 public void leaderChanged(boolean isLeader) {
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800161 log.debug("SDN-IP Leader changed: {}", isLeader);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800162
163 if (!isLeader) {
164 this.isElectedLeader = false;
165 this.isActivatedLeader = false;
166 return; // Nothing to do
167 }
168 this.isActivatedLeader = false;
169 this.isElectedLeader = true;
170
171 //
172 // Tell the Intents Synchronizer thread to start the synchronization
173 //
174 intentsSynchronizerSemaphore.release();
175 }
176
177 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800178 * Gets the route intents.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800179 *
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800180 * @return the route intents
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800181 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800182 public Collection<MultiPointToSinglePointIntent> getRouteIntents() {
183 List<MultiPointToSinglePointIntent> result = new LinkedList<>();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800184
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800185 for (Map.Entry<IpPrefix, MultiPointToSinglePointIntent> entry :
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800186 routeIntents.entrySet()) {
187 result.add(entry.getValue());
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800188 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800189 return result;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800190 }
191
192 /**
193 * Thread for Intent Synchronization.
194 */
195 private void doIntentSynchronizationThread() {
196 boolean interrupted = false;
197 try {
198 while (!interrupted) {
199 try {
200 intentsSynchronizerSemaphore.acquire();
201 //
202 // Drain all permits, because a single synchronization is
203 // sufficient.
204 //
205 intentsSynchronizerSemaphore.drainPermits();
206 } catch (InterruptedException e) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800207 interrupted = true;
208 break;
209 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800210 synchronizeIntents();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800211 }
212 } finally {
213 if (interrupted) {
214 Thread.currentThread().interrupt();
215 }
216 }
217 }
218
219 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800220 * Submits a collection of point-to-point intents.
221 *
222 * @param intents the intents to submit
223 */
224 void submitPeerIntents(Collection<PointToPointIntent> intents) {
225 synchronized (this) {
226 // Store the intents in memory
227 for (PointToPointIntent intent : intents) {
228 peerIntents.put(new IntentKey(intent), intent);
229 }
230
231 // Push the intents
232 if (isElectedLeader && isActivatedLeader) {
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800233 log.debug("SDN-IP Submitting all Peer Intents...");
Brian O'Connor72a034c2014-11-26 18:24:23 -0800234 IntentOperations.Builder builder = IntentOperations.builder(appId);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800235 for (Intent intent : intents) {
Pavlin Radoslavovdde22ae2014-11-24 11:47:17 -0800236 builder.addSubmitOperation(intent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800237 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800238 IntentOperations intentOperations = builder.build();
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800239 log.trace("SDN-IP Submitting intents: {}",
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800240 intentOperations.operations());
241 intentService.execute(intentOperations);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800242 }
243 }
244 }
245
246 /**
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800247 * Updates multi-point-to-single-point route intents.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800248 *
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800249 * @param submitIntents the intents to submit
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800250 * @param withdrawPrefixes the IPv4 or IPv6 matching prefixes for the
251 * intents to withdraw
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800252 */
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800253 void updateRouteIntents(
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800254 Collection<Pair<IpPrefix, MultiPointToSinglePointIntent>> submitIntents,
255 Collection<IpPrefix> withdrawPrefixes) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800256
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800257 //
258 // NOTE: Semantically, we MUST withdraw existing intents before
259 // submitting new intents.
260 //
261 synchronized (this) {
262 MultiPointToSinglePointIntent intent;
263
264 log.debug("SDN-IP submitting intents = {} withdrawing = {}",
265 submitIntents.size(), withdrawPrefixes.size());
266
267 //
268 // Prepare the Intent batch operations for the intents to withdraw
269 //
270 IntentOperations.Builder withdrawBuilder =
271 IntentOperations.builder(appId);
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800272 for (IpPrefix prefix : withdrawPrefixes) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800273 intent = routeIntents.remove(prefix);
274 if (intent == null) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800275 log.trace("SDN-IP No intent in routeIntents to delete " +
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800276 "for prefix: {}", prefix);
277 continue;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800278 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800279 if (isElectedLeader && isActivatedLeader) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800280 log.trace("SDN-IP Withdrawing intent: {}", intent);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800281 withdrawBuilder.addWithdrawOperation(intent.id());
282 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800283 }
284
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800285 //
286 // Prepare the Intent batch operations for the intents to submit
287 //
288 IntentOperations.Builder submitBuilder =
289 IntentOperations.builder(appId);
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800290 for (Pair<IpPrefix, MultiPointToSinglePointIntent> pair :
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800291 submitIntents) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800292 IpPrefix prefix = pair.getLeft();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800293 intent = pair.getRight();
294 MultiPointToSinglePointIntent oldIntent =
295 routeIntents.put(prefix, intent);
296 if (isElectedLeader && isActivatedLeader) {
297 if (oldIntent != null) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800298 log.trace("SDN-IP Withdrawing old intent: {}",
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800299 oldIntent);
300 withdrawBuilder.addWithdrawOperation(oldIntent.id());
301 }
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800302 log.trace("SDN-IP Submitting intent: {}", intent);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800303 submitBuilder.addSubmitOperation(intent);
304 }
305 }
306
307 //
308 // Submit the Intent operations
309 //
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800310 if (isElectedLeader && isActivatedLeader) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800311 IntentOperations intentOperations = withdrawBuilder.build();
312 if (!intentOperations.operations().isEmpty()) {
313 log.debug("SDN-IP Withdrawing intents executed");
314 intentService.execute(intentOperations);
315 }
316 intentOperations = submitBuilder.build();
317 if (!intentOperations.operations().isEmpty()) {
318 log.debug("SDN-IP Submitting intents executed");
319 intentService.execute(intentOperations);
320 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800321 }
322 }
323 }
324
325 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800326 * Synchronize the in-memory Intents with the Intents in the Intent
327 * framework.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800328 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800329 void synchronizeIntents() {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800330 synchronized (this) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800331
332 Map<IntentKey, Intent> localIntents = new HashMap<>();
333 Map<IntentKey, Intent> fetchedIntents = new HashMap<>();
334 Collection<Intent> storeInMemoryIntents = new LinkedList<>();
335 Collection<Intent> addIntents = new LinkedList<>();
336 Collection<Intent> deleteIntents = new LinkedList<>();
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800337 IntentOperations intentOperations;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800338
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800339 if (!isElectedLeader) {
340 return; // Nothing to do: not the leader anymore
341 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800342 log.debug("SDN-IP synchronizing all intents...");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800343
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800344 // Prepare the local intents
345 for (Intent intent : routeIntents.values()) {
346 localIntents.put(new IntentKey(intent), intent);
347 }
348 for (Intent intent : peerIntents.values()) {
349 localIntents.put(new IntentKey(intent), intent);
350 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800351
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800352 // Fetch all intents for this application
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800353 for (Intent intent : intentService.getIntents()) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800354 if (!intent.appId().equals(appId)) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800355 continue;
356 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800357 fetchedIntents.put(new IntentKey(intent), intent);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800358 }
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800359 if (log.isDebugEnabled()) {
360 for (Intent intent: fetchedIntents.values()) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800361 log.trace("SDN-IP Intent Synchronizer: fetched intent: {}",
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800362 intent);
363 }
364 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800365
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800366 computeIntentsDelta(localIntents, fetchedIntents,
367 storeInMemoryIntents, addIntents,
368 deleteIntents);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800369
370 //
371 // Perform the actions:
372 // 1. Store in memory fetched intents that are same. Can be done
373 // even if we are not the leader anymore
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800374 // 2. Delete intents: check if the leader before the operation
375 // 3. Add intents: check if the leader before the operation
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800376 //
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800377 for (Intent intent : storeInMemoryIntents) {
378 // Store the intent in memory based on its type
379 if (intent instanceof MultiPointToSinglePointIntent) {
380 MultiPointToSinglePointIntent mp2pIntent =
381 (MultiPointToSinglePointIntent) intent;
382 // Find the IP prefix
383 Criterion c =
384 mp2pIntent.selector().getCriterion(Criterion.Type.IPV4_DST);
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800385 if (c == null) {
386 // Try IPv6
387 c =
388 mp2pIntent.selector().getCriterion(Criterion.Type.IPV6_DST);
389 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800390 if (c != null && c instanceof IPCriterion) {
391 IPCriterion ipCriterion = (IPCriterion) c;
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800392 IpPrefix ipPrefix = ipCriterion.ip();
393 if (ipPrefix == null) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800394 continue;
395 }
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800396 log.trace("SDN-IP Intent Synchronizer: updating " +
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800397 "in-memory Route Intent for prefix {}",
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800398 ipPrefix);
399 routeIntents.put(ipPrefix, mp2pIntent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800400 } else {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800401 log.warn("SDN-IP no IPV4_DST or IPV6_DST criterion found for Intent {}",
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800402 mp2pIntent.id());
403 }
404 continue;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800405 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800406 if (intent instanceof PointToPointIntent) {
407 PointToPointIntent p2pIntent = (PointToPointIntent) intent;
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800408 log.trace("SDN-IP Intent Synchronizer: updating " +
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800409 "in-memory Peer Intent {}", p2pIntent);
410 peerIntents.put(new IntentKey(intent), p2pIntent);
411 continue;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800412 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800413 }
414
415 // Withdraw Intents
Brian O'Connor72a034c2014-11-26 18:24:23 -0800416 IntentOperations.Builder builder = IntentOperations.builder(appId);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800417 for (Intent intent : deleteIntents) {
418 builder.addWithdrawOperation(intent.id());
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800419 log.trace("SDN-IP Intent Synchronizer: withdrawing intent: {}",
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800420 intent);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800421 }
422 if (!isElectedLeader) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800423 log.trace("SDN-IP Intent Synchronizer: cannot withdraw intents: " +
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800424 "not elected leader anymore");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800425 isActivatedLeader = false;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800426 return;
427 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800428 intentOperations = builder.build();
429 intentService.execute(intentOperations);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800430
431 // Add Intents
Brian O'Connor72a034c2014-11-26 18:24:23 -0800432 builder = IntentOperations.builder(appId);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800433 for (Intent intent : addIntents) {
434 builder.addSubmitOperation(intent);
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800435 log.trace("SDN-IP Intent Synchronizer: submitting intent: {}",
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800436 intent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800437 }
438 if (!isElectedLeader) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800439 log.trace("SDN-IP Intent Synchronizer: cannot submit intents: " +
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800440 "not elected leader anymore");
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800441 isActivatedLeader = false;
442 return;
443 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800444 intentOperations = builder.build();
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800445 intentService.execute(intentOperations);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800446
447 if (isElectedLeader) {
448 isActivatedLeader = true; // Allow push of Intents
449 } else {
450 isActivatedLeader = false;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800451 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800452 log.debug("SDN-IP intent synchronization completed");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800453 }
454 }
455
456 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800457 * Computes the delta in two sets of Intents: local in-memory Intents,
458 * and intents fetched from the Intent framework.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800459 *
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800460 * @param localIntents the local in-memory Intents
461 * @param fetchedIntents the Intents fetched from the Intent framework
462 * @param storeInMemoryIntents the Intents that should be stored in memory.
463 * Note: This Collection must be allocated by the caller, and it will
464 * be populated by this method.
465 * @param addIntents the Intents that should be added to the Intent
466 * framework. Note: This Collection must be allocated by the caller, and
467 * it will be populated by this method.
468 * @param deleteIntents the Intents that should be deleted from the Intent
469 * framework. Note: This Collection must be allocated by the caller, and
470 * it will be populated by this method.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800471 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800472 private void computeIntentsDelta(
473 final Map<IntentKey, Intent> localIntents,
474 final Map<IntentKey, Intent> fetchedIntents,
475 Collection<Intent> storeInMemoryIntents,
476 Collection<Intent> addIntents,
477 Collection<Intent> deleteIntents) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800478
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800479 //
480 // Compute the deltas between the LOCAL in-memory Intents and the
481 // FETCHED Intents:
482 // - If an Intent is in both the LOCAL and FETCHED sets:
483 // If the FETCHED Intent is WITHDRAWING or WITHDRAWN, then
484 // the LOCAL Intent should be added/installed; otherwise the
485 // FETCHED intent should be stored in the local memory
486 // (i.e., override the LOCAL Intent) to preserve the original
487 // Intent ID.
488 // - if a LOCAL Intent is not in the FETCHED set, then the LOCAL
489 // Intent should be added/installed.
490 // - If a FETCHED Intent is not in the LOCAL set, then the FETCHED
491 // Intent should be deleted/withdrawn.
492 //
493 for (Map.Entry<IntentKey, Intent> entry : localIntents.entrySet()) {
494 IntentKey intentKey = entry.getKey();
495 Intent localIntent = entry.getValue();
496 Intent fetchedIntent = fetchedIntents.get(intentKey);
497
498 if (fetchedIntent == null) {
499 //
500 // No FETCHED Intent found: push the LOCAL Intent.
501 //
502 addIntents.add(localIntent);
503 continue;
504 }
505
506 IntentState state =
507 intentService.getIntentState(fetchedIntent.id());
Pavlin Radoslavovdeb8a102014-11-26 13:31:36 -0800508 if (state == null ||
509 state == IntentState.WITHDRAWING ||
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800510 state == IntentState.WITHDRAWN) {
511 // The intent has been withdrawn but according to our route
512 // table it should be installed. We'll reinstall it.
513 addIntents.add(localIntent);
514 continue;
515 }
516 storeInMemoryIntents.add(fetchedIntent);
517 }
518
519 for (Map.Entry<IntentKey, Intent> entry : fetchedIntents.entrySet()) {
520 IntentKey intentKey = entry.getKey();
521 Intent fetchedIntent = entry.getValue();
522 Intent localIntent = localIntents.get(intentKey);
523
524 if (localIntent != null) {
525 continue;
526 }
527
528 IntentState state =
529 intentService.getIntentState(fetchedIntent.id());
Pavlin Radoslavovdeb8a102014-11-26 13:31:36 -0800530 if (state == null ||
531 state == IntentState.WITHDRAWING ||
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800532 state == IntentState.WITHDRAWN) {
533 // Nothing to do. The intent has been already withdrawn.
534 continue;
535 }
536 //
537 // No LOCAL Intent found: delete/withdraw the FETCHED Intent.
538 //
539 deleteIntents.add(fetchedIntent);
540 }
541 }
542
543 /**
544 * Helper class that can be used to compute the key for an Intent by
545 * by excluding the Intent ID.
546 */
547 static final class IntentKey {
548 private final Intent intent;
549
550 /**
551 * Constructor.
552 *
553 * @param intent the intent to use
554 */
555 IntentKey(Intent intent) {
556 checkArgument((intent instanceof MultiPointToSinglePointIntent) ||
557 (intent instanceof PointToPointIntent),
558 "Intent type not recognized", intent);
559 this.intent = intent;
560 }
561
562 /**
563 * Compares two Multi-Point to Single-Point Intents whether they
564 * represent same logical intention.
565 *
566 * @param intent1 the first Intent to compare
567 * @param intent2 the second Intent to compare
568 * @return true if both Intents represent same logical intention,
569 * otherwise false
570 */
571 static boolean equalIntents(MultiPointToSinglePointIntent intent1,
572 MultiPointToSinglePointIntent intent2) {
573 return Objects.equals(intent1.appId(), intent2.appId()) &&
574 Objects.equals(intent1.selector(), intent2.selector()) &&
575 Objects.equals(intent1.treatment(), intent2.treatment()) &&
576 Objects.equals(intent1.ingressPoints(), intent2.ingressPoints()) &&
577 Objects.equals(intent1.egressPoint(), intent2.egressPoint());
578 }
579
580 /**
581 * Compares two Point-to-Point Intents whether they represent
582 * same logical intention.
583 *
584 * @param intent1 the first Intent to compare
585 * @param intent2 the second Intent to compare
586 * @return true if both Intents represent same logical intention,
587 * otherwise false
588 */
589 static boolean equalIntents(PointToPointIntent intent1,
590 PointToPointIntent intent2) {
591 return Objects.equals(intent1.appId(), intent2.appId()) &&
592 Objects.equals(intent1.selector(), intent2.selector()) &&
593 Objects.equals(intent1.treatment(), intent2.treatment()) &&
594 Objects.equals(intent1.ingressPoint(), intent2.ingressPoint()) &&
595 Objects.equals(intent1.egressPoint(), intent2.egressPoint());
596 }
597
598 @Override
599 public int hashCode() {
600 if (intent instanceof PointToPointIntent) {
601 PointToPointIntent p2pIntent = (PointToPointIntent) intent;
602 return Objects.hash(p2pIntent.appId(),
603 p2pIntent.resources(),
604 p2pIntent.selector(),
605 p2pIntent.treatment(),
606 p2pIntent.constraints(),
607 p2pIntent.ingressPoint(),
608 p2pIntent.egressPoint());
609 }
610 if (intent instanceof MultiPointToSinglePointIntent) {
611 MultiPointToSinglePointIntent m2pIntent =
612 (MultiPointToSinglePointIntent) intent;
613 return Objects.hash(m2pIntent.appId(),
614 m2pIntent.resources(),
615 m2pIntent.selector(),
616 m2pIntent.treatment(),
617 m2pIntent.constraints(),
618 m2pIntent.ingressPoints(),
619 m2pIntent.egressPoint());
620 }
621 checkArgument(false, "Intent type not recognized", intent);
622 return 0;
623 }
624
625 @Override
626 public boolean equals(Object obj) {
627 if (this == obj) {
628 return true;
629 }
630 if ((obj == null) || (!(obj instanceof IntentKey))) {
631 return false;
632 }
633 IntentKey other = (IntentKey) obj;
634
635 if (this.intent instanceof PointToPointIntent) {
636 if (!(other.intent instanceof PointToPointIntent)) {
637 return false;
638 }
639 return equalIntents((PointToPointIntent) this.intent,
640 (PointToPointIntent) other.intent);
641 }
642 if (this.intent instanceof MultiPointToSinglePointIntent) {
643 if (!(other.intent instanceof MultiPointToSinglePointIntent)) {
644 return false;
645 }
646 return equalIntents(
647 (MultiPointToSinglePointIntent) this.intent,
648 (MultiPointToSinglePointIntent) other.intent);
649 }
650 checkArgument(false, "Intent type not recognized", intent);
651 return false;
652 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800653 }
654}