blob: d6b7412fcf396aaabfbc806d05a2f302db78ab43 [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;
Jonathan Hart51372182014-12-03 21:32:34 -080032import org.onlab.packet.Ip4Prefix;
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;
58 private final Map<Ip4Prefix, 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
185 for (Map.Entry<Ip4Prefix, 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
250 * @param withdrawPrefixes the IPv4 matching prefixes for the intents
251 * to withdraw
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800252 */
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800253 void updateRouteIntents(
254 Collection<Pair<Ip4Prefix, MultiPointToSinglePointIntent>> submitIntents,
255 Collection<Ip4Prefix> 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);
272 for (Ip4Prefix prefix : withdrawPrefixes) {
273 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);
290 for (Pair<Ip4Prefix, MultiPointToSinglePointIntent> pair :
291 submitIntents) {
292 Ip4Prefix prefix = pair.getLeft();
293 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);
385 if (c != null && c instanceof IPCriterion) {
386 IPCriterion ipCriterion = (IPCriterion) c;
387 Ip4Prefix ip4Prefix = ipCriterion.ip().getIp4Prefix();
388 if (ip4Prefix == null) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800389 continue;
390 }
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800391 log.trace("SDN-IP Intent Synchronizer: updating " +
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800392 "in-memory Route Intent for prefix {}",
393 ip4Prefix);
394 routeIntents.put(ip4Prefix, mp2pIntent);
395 } else {
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800396 log.warn("SDN-IP no IPV4_DST criterion found for Intent {}",
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800397 mp2pIntent.id());
398 }
399 continue;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800400 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800401 if (intent instanceof PointToPointIntent) {
402 PointToPointIntent p2pIntent = (PointToPointIntent) intent;
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800403 log.trace("SDN-IP Intent Synchronizer: updating " +
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800404 "in-memory Peer Intent {}", p2pIntent);
405 peerIntents.put(new IntentKey(intent), p2pIntent);
406 continue;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800407 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800408 }
409
410 // Withdraw Intents
Brian O'Connor72a034c2014-11-26 18:24:23 -0800411 IntentOperations.Builder builder = IntentOperations.builder(appId);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800412 for (Intent intent : deleteIntents) {
413 builder.addWithdrawOperation(intent.id());
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800414 log.trace("SDN-IP Intent Synchronizer: withdrawing intent: {}",
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800415 intent);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800416 }
417 if (!isElectedLeader) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800418 log.trace("SDN-IP Intent Synchronizer: cannot withdraw intents: " +
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800419 "not elected leader anymore");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800420 isActivatedLeader = false;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800421 return;
422 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800423 intentOperations = builder.build();
424 intentService.execute(intentOperations);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800425
426 // Add Intents
Brian O'Connor72a034c2014-11-26 18:24:23 -0800427 builder = IntentOperations.builder(appId);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800428 for (Intent intent : addIntents) {
429 builder.addSubmitOperation(intent);
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800430 log.trace("SDN-IP Intent Synchronizer: submitting intent: {}",
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800431 intent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800432 }
433 if (!isElectedLeader) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800434 log.trace("SDN-IP Intent Synchronizer: cannot submit intents: " +
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800435 "not elected leader anymore");
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800436 isActivatedLeader = false;
437 return;
438 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800439 intentOperations = builder.build();
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800440 intentService.execute(intentOperations);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800441
442 if (isElectedLeader) {
443 isActivatedLeader = true; // Allow push of Intents
444 } else {
445 isActivatedLeader = false;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800446 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800447 log.debug("SDN-IP intent synchronization completed");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800448 }
449 }
450
451 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800452 * Computes the delta in two sets of Intents: local in-memory Intents,
453 * and intents fetched from the Intent framework.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800454 *
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800455 * @param localIntents the local in-memory Intents
456 * @param fetchedIntents the Intents fetched from the Intent framework
457 * @param storeInMemoryIntents the Intents that should be stored in memory.
458 * Note: This Collection must be allocated by the caller, and it will
459 * be populated by this method.
460 * @param addIntents the Intents that should be added to the Intent
461 * framework. Note: This Collection must be allocated by the caller, and
462 * it will be populated by this method.
463 * @param deleteIntents the Intents that should be deleted from the Intent
464 * framework. Note: This Collection must be allocated by the caller, and
465 * it will be populated by this method.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800466 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800467 private void computeIntentsDelta(
468 final Map<IntentKey, Intent> localIntents,
469 final Map<IntentKey, Intent> fetchedIntents,
470 Collection<Intent> storeInMemoryIntents,
471 Collection<Intent> addIntents,
472 Collection<Intent> deleteIntents) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800473
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800474 //
475 // Compute the deltas between the LOCAL in-memory Intents and the
476 // FETCHED Intents:
477 // - If an Intent is in both the LOCAL and FETCHED sets:
478 // If the FETCHED Intent is WITHDRAWING or WITHDRAWN, then
479 // the LOCAL Intent should be added/installed; otherwise the
480 // FETCHED intent should be stored in the local memory
481 // (i.e., override the LOCAL Intent) to preserve the original
482 // Intent ID.
483 // - if a LOCAL Intent is not in the FETCHED set, then the LOCAL
484 // Intent should be added/installed.
485 // - If a FETCHED Intent is not in the LOCAL set, then the FETCHED
486 // Intent should be deleted/withdrawn.
487 //
488 for (Map.Entry<IntentKey, Intent> entry : localIntents.entrySet()) {
489 IntentKey intentKey = entry.getKey();
490 Intent localIntent = entry.getValue();
491 Intent fetchedIntent = fetchedIntents.get(intentKey);
492
493 if (fetchedIntent == null) {
494 //
495 // No FETCHED Intent found: push the LOCAL Intent.
496 //
497 addIntents.add(localIntent);
498 continue;
499 }
500
501 IntentState state =
502 intentService.getIntentState(fetchedIntent.id());
Pavlin Radoslavovdeb8a102014-11-26 13:31:36 -0800503 if (state == null ||
504 state == IntentState.WITHDRAWING ||
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800505 state == IntentState.WITHDRAWN) {
506 // The intent has been withdrawn but according to our route
507 // table it should be installed. We'll reinstall it.
508 addIntents.add(localIntent);
509 continue;
510 }
511 storeInMemoryIntents.add(fetchedIntent);
512 }
513
514 for (Map.Entry<IntentKey, Intent> entry : fetchedIntents.entrySet()) {
515 IntentKey intentKey = entry.getKey();
516 Intent fetchedIntent = entry.getValue();
517 Intent localIntent = localIntents.get(intentKey);
518
519 if (localIntent != null) {
520 continue;
521 }
522
523 IntentState state =
524 intentService.getIntentState(fetchedIntent.id());
Pavlin Radoslavovdeb8a102014-11-26 13:31:36 -0800525 if (state == null ||
526 state == IntentState.WITHDRAWING ||
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800527 state == IntentState.WITHDRAWN) {
528 // Nothing to do. The intent has been already withdrawn.
529 continue;
530 }
531 //
532 // No LOCAL Intent found: delete/withdraw the FETCHED Intent.
533 //
534 deleteIntents.add(fetchedIntent);
535 }
536 }
537
538 /**
539 * Helper class that can be used to compute the key for an Intent by
540 * by excluding the Intent ID.
541 */
542 static final class IntentKey {
543 private final Intent intent;
544
545 /**
546 * Constructor.
547 *
548 * @param intent the intent to use
549 */
550 IntentKey(Intent intent) {
551 checkArgument((intent instanceof MultiPointToSinglePointIntent) ||
552 (intent instanceof PointToPointIntent),
553 "Intent type not recognized", intent);
554 this.intent = intent;
555 }
556
557 /**
558 * Compares two Multi-Point to Single-Point Intents whether they
559 * represent same logical intention.
560 *
561 * @param intent1 the first Intent to compare
562 * @param intent2 the second Intent to compare
563 * @return true if both Intents represent same logical intention,
564 * otherwise false
565 */
566 static boolean equalIntents(MultiPointToSinglePointIntent intent1,
567 MultiPointToSinglePointIntent intent2) {
568 return Objects.equals(intent1.appId(), intent2.appId()) &&
569 Objects.equals(intent1.selector(), intent2.selector()) &&
570 Objects.equals(intent1.treatment(), intent2.treatment()) &&
571 Objects.equals(intent1.ingressPoints(), intent2.ingressPoints()) &&
572 Objects.equals(intent1.egressPoint(), intent2.egressPoint());
573 }
574
575 /**
576 * Compares two Point-to-Point Intents whether they represent
577 * same logical intention.
578 *
579 * @param intent1 the first Intent to compare
580 * @param intent2 the second Intent to compare
581 * @return true if both Intents represent same logical intention,
582 * otherwise false
583 */
584 static boolean equalIntents(PointToPointIntent intent1,
585 PointToPointIntent intent2) {
586 return Objects.equals(intent1.appId(), intent2.appId()) &&
587 Objects.equals(intent1.selector(), intent2.selector()) &&
588 Objects.equals(intent1.treatment(), intent2.treatment()) &&
589 Objects.equals(intent1.ingressPoint(), intent2.ingressPoint()) &&
590 Objects.equals(intent1.egressPoint(), intent2.egressPoint());
591 }
592
593 @Override
594 public int hashCode() {
595 if (intent instanceof PointToPointIntent) {
596 PointToPointIntent p2pIntent = (PointToPointIntent) intent;
597 return Objects.hash(p2pIntent.appId(),
598 p2pIntent.resources(),
599 p2pIntent.selector(),
600 p2pIntent.treatment(),
601 p2pIntent.constraints(),
602 p2pIntent.ingressPoint(),
603 p2pIntent.egressPoint());
604 }
605 if (intent instanceof MultiPointToSinglePointIntent) {
606 MultiPointToSinglePointIntent m2pIntent =
607 (MultiPointToSinglePointIntent) intent;
608 return Objects.hash(m2pIntent.appId(),
609 m2pIntent.resources(),
610 m2pIntent.selector(),
611 m2pIntent.treatment(),
612 m2pIntent.constraints(),
613 m2pIntent.ingressPoints(),
614 m2pIntent.egressPoint());
615 }
616 checkArgument(false, "Intent type not recognized", intent);
617 return 0;
618 }
619
620 @Override
621 public boolean equals(Object obj) {
622 if (this == obj) {
623 return true;
624 }
625 if ((obj == null) || (!(obj instanceof IntentKey))) {
626 return false;
627 }
628 IntentKey other = (IntentKey) obj;
629
630 if (this.intent instanceof PointToPointIntent) {
631 if (!(other.intent instanceof PointToPointIntent)) {
632 return false;
633 }
634 return equalIntents((PointToPointIntent) this.intent,
635 (PointToPointIntent) other.intent);
636 }
637 if (this.intent instanceof MultiPointToSinglePointIntent) {
638 if (!(other.intent instanceof MultiPointToSinglePointIntent)) {
639 return false;
640 }
641 return equalIntents(
642 (MultiPointToSinglePointIntent) this.intent,
643 (MultiPointToSinglePointIntent) other.intent);
644 }
645 checkArgument(false, "Intent type not recognized", intent);
646 return false;
647 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800648 }
649}