blob: 7110e4ab8ecf508e208517c291e63e84ff6bc252 [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 Radoslavov248c2ae2014-12-02 09:51:25 -080029import org.apache.commons.lang3.tuple.Pair;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080030import org.onlab.onos.core.ApplicationId;
31import org.onlab.onos.net.flow.criteria.Criteria.IPCriterion;
32import org.onlab.onos.net.flow.criteria.Criterion;
33import org.onlab.onos.net.intent.Intent;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -080034import org.onlab.onos.net.intent.IntentOperations;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080035import org.onlab.onos.net.intent.IntentService;
36import org.onlab.onos.net.intent.IntentState;
37import org.onlab.onos.net.intent.MultiPointToSinglePointIntent;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -080038import org.onlab.onos.net.intent.PointToPointIntent;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080039import org.onlab.packet.Ip4Prefix;
40import org.slf4j.Logger;
41import org.slf4j.LoggerFactory;
42
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080043import com.google.common.util.concurrent.ThreadFactoryBuilder;
44
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -080045import static com.google.common.base.Preconditions.checkArgument;
46
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080047public class IntentSynchronizer {
48 private static final Logger log =
49 LoggerFactory.getLogger(IntentSynchronizer.class);
50
51 private final ApplicationId appId;
52 private final IntentService intentService;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -080053 private final Map<IntentKey, PointToPointIntent> peerIntents;
54 private final Map<Ip4Prefix, MultiPointToSinglePointIntent> routeIntents;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080055
56 //
57 // State to deal with SDN-IP Leader election and pushing Intents
58 //
59 private final ExecutorService bgpIntentsSynchronizerExecutor;
60 private final Semaphore intentsSynchronizerSemaphore = new Semaphore(0);
61 private volatile boolean isElectedLeader = false;
62 private volatile boolean isActivatedLeader = false;
63
64 /**
65 * Class constructor.
66 *
67 * @param appId the Application ID
68 * @param intentService the intent service
69 */
70 IntentSynchronizer(ApplicationId appId, IntentService intentService) {
71 this.appId = appId;
72 this.intentService = intentService;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -080073 peerIntents = new ConcurrentHashMap<>();
74 routeIntents = new ConcurrentHashMap<>();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080075
76 bgpIntentsSynchronizerExecutor = Executors.newSingleThreadExecutor(
77 new ThreadFactoryBuilder()
Pavlin Radoslavov8b752442014-11-18 14:34:37 -080078 .setNameFormat("sdnip-intents-synchronizer-%d").build());
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080079 }
80
81 /**
82 * Starts the synchronizer.
83 */
84 public void start() {
85 bgpIntentsSynchronizerExecutor.execute(new Runnable() {
86 @Override
87 public void run() {
88 doIntentSynchronizationThread();
89 }
90 });
91 }
92
93 /**
94 * Stops the synchronizer.
95 */
96 public void stop() {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -080097 synchronized (this) {
98 // Stop the thread(s)
99 bgpIntentsSynchronizerExecutor.shutdownNow();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800100
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800101 //
102 // Withdraw all SDN-IP intents
103 //
104 if (!isElectedLeader) {
105 return; // Nothing to do: not the leader anymore
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800106 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800107
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800108 //
Pavlin Radoslavov20be3e62014-11-25 18:52:08 -0800109 // NOTE: We don't withdraw the intents during shutdown, because
110 // it creates flux in the data plane during switchover.
111 //
112
113 /*
114 //
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800115 // Build a batch operation to withdraw all intents from this
116 // application.
117 //
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800118 log.debug("SDN-IP Intent Synchronizer shutdown: " +
119 "withdrawing all intents...");
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800120 IntentOperations.Builder builder = IntentOperations.builder(appId);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800121 for (Intent intent : intentService.getIntents()) {
122 // Skip the intents from other applications
123 if (!intent.appId().equals(appId)) {
124 continue;
125 }
126
127 // Skip the intents that are already withdrawn
128 IntentState intentState =
129 intentService.getIntentState(intent.id());
Pavlin Radoslavovdeb8a102014-11-26 13:31:36 -0800130 if ((intentState == null) ||
131 intentState.equals(IntentState.WITHDRAWING) ||
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800132 intentState.equals(IntentState.WITHDRAWN)) {
133 continue;
134 }
135
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800136 log.trace("SDN-IP Intent Synchronizer withdrawing intent: {}",
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800137 intent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800138 builder.addWithdrawOperation(intent.id());
139 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800140 IntentOperations intentOperations = builder.build();
141 intentService.execute(intentOperations);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800142 leaderChanged(false);
143
144 peerIntents.clear();
145 routeIntents.clear();
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800146 log.debug("SDN-IP Intent Synchronizer shutdown completed");
Pavlin Radoslavov20be3e62014-11-25 18:52:08 -0800147 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800148 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800149 }
150
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800151 public void leaderChanged(boolean isLeader) {
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800152 log.debug("SDN-IP Leader changed: {}", isLeader);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800153
154 if (!isLeader) {
155 this.isElectedLeader = false;
156 this.isActivatedLeader = false;
157 return; // Nothing to do
158 }
159 this.isActivatedLeader = false;
160 this.isElectedLeader = true;
161
162 //
163 // Tell the Intents Synchronizer thread to start the synchronization
164 //
165 intentsSynchronizerSemaphore.release();
166 }
167
168 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800169 * Gets the route intents.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800170 *
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800171 * @return the route intents
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800172 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800173 public Collection<MultiPointToSinglePointIntent> getRouteIntents() {
174 List<MultiPointToSinglePointIntent> result = new LinkedList<>();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800175
176 for (Map.Entry<Ip4Prefix, MultiPointToSinglePointIntent> entry :
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800177 routeIntents.entrySet()) {
178 result.add(entry.getValue());
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800179 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800180 return result;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800181 }
182
183 /**
184 * Thread for Intent Synchronization.
185 */
186 private void doIntentSynchronizationThread() {
187 boolean interrupted = false;
188 try {
189 while (!interrupted) {
190 try {
191 intentsSynchronizerSemaphore.acquire();
192 //
193 // Drain all permits, because a single synchronization is
194 // sufficient.
195 //
196 intentsSynchronizerSemaphore.drainPermits();
197 } catch (InterruptedException e) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800198 interrupted = true;
199 break;
200 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800201 synchronizeIntents();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800202 }
203 } finally {
204 if (interrupted) {
205 Thread.currentThread().interrupt();
206 }
207 }
208 }
209
210 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800211 * Submits a collection of point-to-point intents.
212 *
213 * @param intents the intents to submit
214 */
215 void submitPeerIntents(Collection<PointToPointIntent> intents) {
216 synchronized (this) {
217 // Store the intents in memory
218 for (PointToPointIntent intent : intents) {
219 peerIntents.put(new IntentKey(intent), intent);
220 }
221
222 // Push the intents
223 if (isElectedLeader && isActivatedLeader) {
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800224 log.debug("SDN-IP Submitting all Peer Intents...");
Brian O'Connor72a034c2014-11-26 18:24:23 -0800225 IntentOperations.Builder builder = IntentOperations.builder(appId);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800226 for (Intent intent : intents) {
Pavlin Radoslavovdde22ae2014-11-24 11:47:17 -0800227 builder.addSubmitOperation(intent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800228 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800229 IntentOperations intentOperations = builder.build();
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800230 log.trace("SDN-IP Submitting intents: {}",
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800231 intentOperations.operations());
232 intentService.execute(intentOperations);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800233 }
234 }
235 }
236
237 /**
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800238 * Updates multi-point-to-single-point route intents.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800239 *
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800240 * @param submitIntents the intents to submit
241 * @param withdrawPrefixes the IPv4 matching prefixes for the intents
242 * to withdraw
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800243 */
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800244 void updateRouteIntents(
245 Collection<Pair<Ip4Prefix, MultiPointToSinglePointIntent>> submitIntents,
246 Collection<Ip4Prefix> withdrawPrefixes) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800247
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800248 //
249 // NOTE: Semantically, we MUST withdraw existing intents before
250 // submitting new intents.
251 //
252 synchronized (this) {
253 MultiPointToSinglePointIntent intent;
254
255 log.debug("SDN-IP submitting intents = {} withdrawing = {}",
256 submitIntents.size(), withdrawPrefixes.size());
257
258 //
259 // Prepare the Intent batch operations for the intents to withdraw
260 //
261 IntentOperations.Builder withdrawBuilder =
262 IntentOperations.builder(appId);
263 for (Ip4Prefix prefix : withdrawPrefixes) {
264 intent = routeIntents.remove(prefix);
265 if (intent == null) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800266 log.trace("SDN-IP No intent in routeIntents to delete " +
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800267 "for prefix: {}", prefix);
268 continue;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800269 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800270 if (isElectedLeader && isActivatedLeader) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800271 log.trace("SDN-IP Withdrawing intent: {}", intent);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800272 withdrawBuilder.addWithdrawOperation(intent.id());
273 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800274 }
275
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800276 //
277 // Prepare the Intent batch operations for the intents to submit
278 //
279 IntentOperations.Builder submitBuilder =
280 IntentOperations.builder(appId);
281 for (Pair<Ip4Prefix, MultiPointToSinglePointIntent> pair :
282 submitIntents) {
283 Ip4Prefix prefix = pair.getLeft();
284 intent = pair.getRight();
285 MultiPointToSinglePointIntent oldIntent =
286 routeIntents.put(prefix, intent);
287 if (isElectedLeader && isActivatedLeader) {
288 if (oldIntent != null) {
289 //
290 // TODO: Short-term solution to explicitly withdraw
291 // instead of using "replace" operation.
292 //
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800293 log.trace("SDN-IP Withdrawing old intent: {}",
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800294 oldIntent);
295 withdrawBuilder.addWithdrawOperation(oldIntent.id());
296 }
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800297 log.trace("SDN-IP Submitting intent: {}", intent);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800298 submitBuilder.addSubmitOperation(intent);
299 }
300 }
301
302 //
303 // Submit the Intent operations
304 //
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800305 if (isElectedLeader && isActivatedLeader) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800306 IntentOperations intentOperations = withdrawBuilder.build();
307 if (!intentOperations.operations().isEmpty()) {
308 log.debug("SDN-IP Withdrawing intents executed");
309 intentService.execute(intentOperations);
310 }
311 intentOperations = submitBuilder.build();
312 if (!intentOperations.operations().isEmpty()) {
313 log.debug("SDN-IP Submitting intents executed");
314 intentService.execute(intentOperations);
315 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800316 }
317 }
318 }
319
320 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800321 * Synchronize the in-memory Intents with the Intents in the Intent
322 * framework.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800323 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800324 void synchronizeIntents() {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800325 synchronized (this) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800326
327 Map<IntentKey, Intent> localIntents = new HashMap<>();
328 Map<IntentKey, Intent> fetchedIntents = new HashMap<>();
329 Collection<Intent> storeInMemoryIntents = new LinkedList<>();
330 Collection<Intent> addIntents = new LinkedList<>();
331 Collection<Intent> deleteIntents = new LinkedList<>();
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800332 IntentOperations intentOperations;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800333
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800334 if (!isElectedLeader) {
335 return; // Nothing to do: not the leader anymore
336 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800337 log.debug("SDN-IP synchronizing all intents...");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800338
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800339 // Prepare the local intents
340 for (Intent intent : routeIntents.values()) {
341 localIntents.put(new IntentKey(intent), intent);
342 }
343 for (Intent intent : peerIntents.values()) {
344 localIntents.put(new IntentKey(intent), intent);
345 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800346
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800347 // Fetch all intents for this application
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800348 for (Intent intent : intentService.getIntents()) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800349 if (!intent.appId().equals(appId)) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800350 continue;
351 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800352 fetchedIntents.put(new IntentKey(intent), intent);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800353 }
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800354 if (log.isDebugEnabled()) {
355 for (Intent intent: fetchedIntents.values()) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800356 log.trace("SDN-IP Intent Synchronizer: fetched intent: {}",
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800357 intent);
358 }
359 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800360
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800361 computeIntentsDelta(localIntents, fetchedIntents,
362 storeInMemoryIntents, addIntents,
363 deleteIntents);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800364
365 //
366 // Perform the actions:
367 // 1. Store in memory fetched intents that are same. Can be done
368 // even if we are not the leader anymore
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800369 // 2. Delete intents: check if the leader before the operation
370 // 3. Add intents: check if the leader before the operation
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800371 //
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800372 for (Intent intent : storeInMemoryIntents) {
373 // Store the intent in memory based on its type
374 if (intent instanceof MultiPointToSinglePointIntent) {
375 MultiPointToSinglePointIntent mp2pIntent =
376 (MultiPointToSinglePointIntent) intent;
377 // Find the IP prefix
378 Criterion c =
379 mp2pIntent.selector().getCriterion(Criterion.Type.IPV4_DST);
380 if (c != null && c instanceof IPCriterion) {
381 IPCriterion ipCriterion = (IPCriterion) c;
382 Ip4Prefix ip4Prefix = ipCriterion.ip().getIp4Prefix();
383 if (ip4Prefix == null) {
384 // TODO: For now we support only IPv4
385 continue;
386 }
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800387 log.trace("SDN-IP Intent Synchronizer: updating " +
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800388 "in-memory Route Intent for prefix {}",
389 ip4Prefix);
390 routeIntents.put(ip4Prefix, mp2pIntent);
391 } else {
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800392 log.warn("SDN-IP no IPV4_DST criterion found for Intent {}",
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800393 mp2pIntent.id());
394 }
395 continue;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800396 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800397 if (intent instanceof PointToPointIntent) {
398 PointToPointIntent p2pIntent = (PointToPointIntent) intent;
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800399 log.trace("SDN-IP Intent Synchronizer: updating " +
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800400 "in-memory Peer Intent {}", p2pIntent);
401 peerIntents.put(new IntentKey(intent), p2pIntent);
402 continue;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800403 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800404 }
405
406 // Withdraw Intents
Brian O'Connor72a034c2014-11-26 18:24:23 -0800407 IntentOperations.Builder builder = IntentOperations.builder(appId);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800408 for (Intent intent : deleteIntents) {
409 builder.addWithdrawOperation(intent.id());
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800410 log.trace("SDN-IP Intent Synchronizer: withdrawing intent: {}",
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800411 intent);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800412 }
413 if (!isElectedLeader) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800414 log.trace("SDN-IP Intent Synchronizer: cannot withdraw intents: " +
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800415 "not elected leader anymore");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800416 isActivatedLeader = false;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800417 return;
418 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800419 intentOperations = builder.build();
420 intentService.execute(intentOperations);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800421
422 // Add Intents
Brian O'Connor72a034c2014-11-26 18:24:23 -0800423 builder = IntentOperations.builder(appId);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800424 for (Intent intent : addIntents) {
425 builder.addSubmitOperation(intent);
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800426 log.trace("SDN-IP Intent Synchronizer: submitting intent: {}",
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800427 intent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800428 }
429 if (!isElectedLeader) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800430 log.trace("SDN-IP Intent Synchronizer: cannot submit intents: " +
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800431 "not elected leader anymore");
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800432 isActivatedLeader = false;
433 return;
434 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800435 intentOperations = builder.build();
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800436 intentService.execute(intentOperations);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800437
438 if (isElectedLeader) {
439 isActivatedLeader = true; // Allow push of Intents
440 } else {
441 isActivatedLeader = false;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800442 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800443 log.debug("SDN-IP intent synchronization completed");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800444 }
445 }
446
447 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800448 * Computes the delta in two sets of Intents: local in-memory Intents,
449 * and intents fetched from the Intent framework.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800450 *
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800451 * @param localIntents the local in-memory Intents
452 * @param fetchedIntents the Intents fetched from the Intent framework
453 * @param storeInMemoryIntents the Intents that should be stored in memory.
454 * Note: This Collection must be allocated by the caller, and it will
455 * be populated by this method.
456 * @param addIntents the Intents that should be added to the Intent
457 * framework. Note: This Collection must be allocated by the caller, and
458 * it will be populated by this method.
459 * @param deleteIntents the Intents that should be deleted from the Intent
460 * framework. Note: This Collection must be allocated by the caller, and
461 * it will be populated by this method.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800462 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800463 private void computeIntentsDelta(
464 final Map<IntentKey, Intent> localIntents,
465 final Map<IntentKey, Intent> fetchedIntents,
466 Collection<Intent> storeInMemoryIntents,
467 Collection<Intent> addIntents,
468 Collection<Intent> deleteIntents) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800469
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800470 //
471 // Compute the deltas between the LOCAL in-memory Intents and the
472 // FETCHED Intents:
473 // - If an Intent is in both the LOCAL and FETCHED sets:
474 // If the FETCHED Intent is WITHDRAWING or WITHDRAWN, then
475 // the LOCAL Intent should be added/installed; otherwise the
476 // FETCHED intent should be stored in the local memory
477 // (i.e., override the LOCAL Intent) to preserve the original
478 // Intent ID.
479 // - if a LOCAL Intent is not in the FETCHED set, then the LOCAL
480 // Intent should be added/installed.
481 // - If a FETCHED Intent is not in the LOCAL set, then the FETCHED
482 // Intent should be deleted/withdrawn.
483 //
484 for (Map.Entry<IntentKey, Intent> entry : localIntents.entrySet()) {
485 IntentKey intentKey = entry.getKey();
486 Intent localIntent = entry.getValue();
487 Intent fetchedIntent = fetchedIntents.get(intentKey);
488
489 if (fetchedIntent == null) {
490 //
491 // No FETCHED Intent found: push the LOCAL Intent.
492 //
493 addIntents.add(localIntent);
494 continue;
495 }
496
497 IntentState state =
498 intentService.getIntentState(fetchedIntent.id());
Pavlin Radoslavovdeb8a102014-11-26 13:31:36 -0800499 if (state == null ||
500 state == IntentState.WITHDRAWING ||
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800501 state == IntentState.WITHDRAWN) {
502 // The intent has been withdrawn but according to our route
503 // table it should be installed. We'll reinstall it.
504 addIntents.add(localIntent);
505 continue;
506 }
507 storeInMemoryIntents.add(fetchedIntent);
508 }
509
510 for (Map.Entry<IntentKey, Intent> entry : fetchedIntents.entrySet()) {
511 IntentKey intentKey = entry.getKey();
512 Intent fetchedIntent = entry.getValue();
513 Intent localIntent = localIntents.get(intentKey);
514
515 if (localIntent != null) {
516 continue;
517 }
518
519 IntentState state =
520 intentService.getIntentState(fetchedIntent.id());
Pavlin Radoslavovdeb8a102014-11-26 13:31:36 -0800521 if (state == null ||
522 state == IntentState.WITHDRAWING ||
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800523 state == IntentState.WITHDRAWN) {
524 // Nothing to do. The intent has been already withdrawn.
525 continue;
526 }
527 //
528 // No LOCAL Intent found: delete/withdraw the FETCHED Intent.
529 //
530 deleteIntents.add(fetchedIntent);
531 }
532 }
533
534 /**
535 * Helper class that can be used to compute the key for an Intent by
536 * by excluding the Intent ID.
537 */
538 static final class IntentKey {
539 private final Intent intent;
540
541 /**
542 * Constructor.
543 *
544 * @param intent the intent to use
545 */
546 IntentKey(Intent intent) {
547 checkArgument((intent instanceof MultiPointToSinglePointIntent) ||
548 (intent instanceof PointToPointIntent),
549 "Intent type not recognized", intent);
550 this.intent = intent;
551 }
552
553 /**
554 * Compares two Multi-Point to Single-Point Intents whether they
555 * represent same logical intention.
556 *
557 * @param intent1 the first Intent to compare
558 * @param intent2 the second Intent to compare
559 * @return true if both Intents represent same logical intention,
560 * otherwise false
561 */
562 static boolean equalIntents(MultiPointToSinglePointIntent intent1,
563 MultiPointToSinglePointIntent intent2) {
564 return Objects.equals(intent1.appId(), intent2.appId()) &&
565 Objects.equals(intent1.selector(), intent2.selector()) &&
566 Objects.equals(intent1.treatment(), intent2.treatment()) &&
567 Objects.equals(intent1.ingressPoints(), intent2.ingressPoints()) &&
568 Objects.equals(intent1.egressPoint(), intent2.egressPoint());
569 }
570
571 /**
572 * Compares two Point-to-Point Intents whether they represent
573 * same logical intention.
574 *
575 * @param intent1 the first Intent to compare
576 * @param intent2 the second Intent to compare
577 * @return true if both Intents represent same logical intention,
578 * otherwise false
579 */
580 static boolean equalIntents(PointToPointIntent intent1,
581 PointToPointIntent intent2) {
582 return Objects.equals(intent1.appId(), intent2.appId()) &&
583 Objects.equals(intent1.selector(), intent2.selector()) &&
584 Objects.equals(intent1.treatment(), intent2.treatment()) &&
585 Objects.equals(intent1.ingressPoint(), intent2.ingressPoint()) &&
586 Objects.equals(intent1.egressPoint(), intent2.egressPoint());
587 }
588
589 @Override
590 public int hashCode() {
591 if (intent instanceof PointToPointIntent) {
592 PointToPointIntent p2pIntent = (PointToPointIntent) intent;
593 return Objects.hash(p2pIntent.appId(),
594 p2pIntent.resources(),
595 p2pIntent.selector(),
596 p2pIntent.treatment(),
597 p2pIntent.constraints(),
598 p2pIntent.ingressPoint(),
599 p2pIntent.egressPoint());
600 }
601 if (intent instanceof MultiPointToSinglePointIntent) {
602 MultiPointToSinglePointIntent m2pIntent =
603 (MultiPointToSinglePointIntent) intent;
604 return Objects.hash(m2pIntent.appId(),
605 m2pIntent.resources(),
606 m2pIntent.selector(),
607 m2pIntent.treatment(),
608 m2pIntent.constraints(),
609 m2pIntent.ingressPoints(),
610 m2pIntent.egressPoint());
611 }
612 checkArgument(false, "Intent type not recognized", intent);
613 return 0;
614 }
615
616 @Override
617 public boolean equals(Object obj) {
618 if (this == obj) {
619 return true;
620 }
621 if ((obj == null) || (!(obj instanceof IntentKey))) {
622 return false;
623 }
624 IntentKey other = (IntentKey) obj;
625
626 if (this.intent instanceof PointToPointIntent) {
627 if (!(other.intent instanceof PointToPointIntent)) {
628 return false;
629 }
630 return equalIntents((PointToPointIntent) this.intent,
631 (PointToPointIntent) other.intent);
632 }
633 if (this.intent instanceof MultiPointToSinglePointIntent) {
634 if (!(other.intent instanceof MultiPointToSinglePointIntent)) {
635 return false;
636 }
637 return equalIntents(
638 (MultiPointToSinglePointIntent) this.intent,
639 (MultiPointToSinglePointIntent) other.intent);
640 }
641 checkArgument(false, "Intent type not recognized", intent);
642 return false;
643 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800644 }
645}