blob: e0e9faf0c5e24f3820c77893afc0be533d929c42 [file] [log] [blame]
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -08001/*
Ray Milkey34c95902015-04-15 09:47:53 -07002 * Copyright 2014-2015 Open Networking Laboratory
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -08003 *
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 Hart96c5a4a2015-07-31 14:23:33 -070018import com.google.common.collect.ImmutableList;
19import com.google.common.util.concurrent.ThreadFactoryBuilder;
20import org.onlab.packet.Ethernet;
21import org.onlab.packet.IpAddress;
22import org.onlab.packet.IpPrefix;
23import org.onlab.packet.MacAddress;
24import org.onlab.packet.VlanId;
25import org.onosproject.core.ApplicationId;
Jonathan Hart4cb39882015-08-12 23:50:55 -040026import org.onosproject.incubator.net.intf.Interface;
27import org.onosproject.incubator.net.intf.InterfaceService;
Jonathan Hart96c5a4a2015-07-31 14:23:33 -070028import org.onosproject.net.ConnectPoint;
29import org.onosproject.net.Host;
30import org.onosproject.net.flow.DefaultTrafficSelector;
31import org.onosproject.net.flow.DefaultTrafficTreatment;
32import org.onosproject.net.flow.TrafficSelector;
33import org.onosproject.net.flow.TrafficTreatment;
34import org.onosproject.net.flow.criteria.Criterion;
35import org.onosproject.net.flow.criteria.IPCriterion;
36import org.onosproject.net.host.HostService;
37import org.onosproject.net.intent.Constraint;
38import org.onosproject.net.intent.Intent;
39import org.onosproject.net.intent.IntentService;
40import org.onosproject.net.intent.IntentState;
41import org.onosproject.net.intent.Key;
42import org.onosproject.net.intent.MultiPointToSinglePointIntent;
43import org.onosproject.net.intent.PointToPointIntent;
44import org.onosproject.net.intent.constraint.PartialFailureConstraint;
45import org.onosproject.routing.FibListener;
46import org.onosproject.routing.FibUpdate;
47import org.onosproject.routing.IntentRequestListener;
Jonathan Hart96c5a4a2015-07-31 14:23:33 -070048import org.onosproject.routing.config.RoutingConfigurationService;
49import org.slf4j.Logger;
50import org.slf4j.LoggerFactory;
51
Ray Milkeyebc5d222015-03-18 15:45:36 -070052import java.util.Collection;
53import java.util.HashMap;
54import java.util.HashSet;
55import java.util.LinkedList;
56import java.util.List;
57import java.util.Map;
58import java.util.Objects;
59import java.util.Set;
60import java.util.concurrent.ConcurrentHashMap;
61import java.util.concurrent.ExecutorService;
62import java.util.concurrent.Executors;
63import java.util.concurrent.Semaphore;
64
Jonathan Hart552e31f2015-02-06 11:11:59 -080065import static com.google.common.base.Preconditions.checkArgument;
Pingping Line28ae4c2015-03-13 11:37:03 -070066import static com.google.common.base.Preconditions.checkNotNull;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080067
Jonathan Hart51372182014-12-03 21:32:34 -080068/**
69 * Synchronizes intents between the in-memory intent store and the
70 * IntentService.
71 */
Pingping Line28ae4c2015-03-13 11:37:03 -070072public class IntentSynchronizer implements FibListener, IntentRequestListener {
Pavlin Radoslavov2aa1f322015-03-11 17:59:44 -070073 private static final int PRIORITY_OFFSET = 100;
74 private static final int PRIORITY_MULTIPLIER = 5;
Jonathan Hart96c5a4a2015-07-31 14:23:33 -070075 protected static final ImmutableList<Constraint> CONSTRAINTS
76 = ImmutableList.of(new PartialFailureConstraint());
Pavlin Radoslavov2aa1f322015-03-11 17:59:44 -070077
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080078 private static final Logger log =
79 LoggerFactory.getLogger(IntentSynchronizer.class);
80
81 private final ApplicationId appId;
82 private final IntentService intentService;
Pingping Line28ae4c2015-03-13 11:37:03 -070083 private final HostService hostService;
Jonathan Hart4cb39882015-08-12 23:50:55 -040084 private final InterfaceService interfaceService;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -080085 private final Map<IntentKey, PointToPointIntent> peerIntents;
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080086 private final Map<IpPrefix, MultiPointToSinglePointIntent> routeIntents;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080087
88 //
89 // State to deal with SDN-IP Leader election and pushing Intents
90 //
91 private final ExecutorService bgpIntentsSynchronizerExecutor;
92 private final Semaphore intentsSynchronizerSemaphore = new Semaphore(0);
93 private volatile boolean isElectedLeader = false;
94 private volatile boolean isActivatedLeader = false;
95
Jonathan Hart90a02c22015-02-13 11:52:07 -080096 private final RoutingConfigurationService configService;
Jonathan Hart552e31f2015-02-06 11:11:59 -080097
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080098 /**
99 * Class constructor.
100 *
101 * @param appId the Application ID
102 * @param intentService the intent service
Pingping Line28ae4c2015-03-13 11:37:03 -0700103 * @param hostService the host service
Jonathan Hart552e31f2015-02-06 11:11:59 -0800104 * @param configService the SDN-IP configuration service
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800105 */
Jonathan Hart552e31f2015-02-06 11:11:59 -0800106 IntentSynchronizer(ApplicationId appId, IntentService intentService,
Pingping Line28ae4c2015-03-13 11:37:03 -0700107 HostService hostService,
Jonathan Hart4cb39882015-08-12 23:50:55 -0400108 RoutingConfigurationService configService,
109 InterfaceService interfaceService) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800110 this.appId = appId;
111 this.intentService = intentService;
Pingping Line28ae4c2015-03-13 11:37:03 -0700112 this.hostService = hostService;
Jonathan Hart4cb39882015-08-12 23:50:55 -0400113 this.interfaceService = interfaceService;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800114 peerIntents = new ConcurrentHashMap<>();
115 routeIntents = new ConcurrentHashMap<>();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800116
Jonathan Hart552e31f2015-02-06 11:11:59 -0800117 this.configService = configService;
Jonathan Hart552e31f2015-02-06 11:11:59 -0800118
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800119 bgpIntentsSynchronizerExecutor = Executors.newSingleThreadExecutor(
120 new ThreadFactoryBuilder()
Pavlin Radoslavov8b752442014-11-18 14:34:37 -0800121 .setNameFormat("sdnip-intents-synchronizer-%d").build());
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800122 }
123
124 /**
125 * Starts the synchronizer.
126 */
127 public void start() {
Jonathan Hart4cb39882015-08-12 23:50:55 -0400128 bgpIntentsSynchronizerExecutor.execute(this::doIntentSynchronizationThread);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800129 }
130
131 /**
132 * Stops the synchronizer.
133 */
134 public void stop() {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800135 synchronized (this) {
136 // Stop the thread(s)
137 bgpIntentsSynchronizerExecutor.shutdownNow();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800138
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800139 //
140 // Withdraw all SDN-IP intents
141 //
142 if (!isElectedLeader) {
143 return; // Nothing to do: not the leader anymore
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800144 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800145
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800146 //
Pavlin Radoslavov20be3e62014-11-25 18:52:08 -0800147 // NOTE: We don't withdraw the intents during shutdown, because
148 // it creates flux in the data plane during switchover.
149 //
150
151 /*
152 //
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800153 // Build a batch operation to withdraw all intents from this
154 // application.
155 //
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800156 log.debug("SDN-IP Intent Synchronizer shutdown: " +
157 "withdrawing all intents...");
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800158 IntentOperations.Builder builder = IntentOperations.builder(appId);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800159 for (Intent intent : intentService.getIntents()) {
160 // Skip the intents from other applications
161 if (!intent.appId().equals(appId)) {
162 continue;
163 }
164
165 // Skip the intents that are already withdrawn
166 IntentState intentState =
167 intentService.getIntentState(intent.id());
Pavlin Radoslavovdeb8a102014-11-26 13:31:36 -0800168 if ((intentState == null) ||
169 intentState.equals(IntentState.WITHDRAWING) ||
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800170 intentState.equals(IntentState.WITHDRAWN)) {
171 continue;
172 }
173
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800174 log.trace("SDN-IP Intent Synchronizer withdrawing intent: {}",
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800175 intent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800176 builder.addWithdrawOperation(intent.id());
177 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800178 IntentOperations intentOperations = builder.build();
179 intentService.execute(intentOperations);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800180 leaderChanged(false);
181
182 peerIntents.clear();
183 routeIntents.clear();
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800184 log.debug("SDN-IP Intent Synchronizer shutdown completed");
Pavlin Radoslavov20be3e62014-11-25 18:52:08 -0800185 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800186 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800187 }
188
Jonathan Hart51372182014-12-03 21:32:34 -0800189 /**
190 * Signals the synchronizer that the SDN-IP leadership has changed.
191 *
192 * @param isLeader true if this instance is now the leader, otherwise false
193 */
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800194 public void leaderChanged(boolean isLeader) {
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800195 log.debug("SDN-IP Leader changed: {}", isLeader);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800196
197 if (!isLeader) {
198 this.isElectedLeader = false;
199 this.isActivatedLeader = false;
200 return; // Nothing to do
201 }
202 this.isActivatedLeader = false;
203 this.isElectedLeader = true;
204
205 //
206 // Tell the Intents Synchronizer thread to start the synchronization
207 //
208 intentsSynchronizerSemaphore.release();
209 }
210
211 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800212 * Gets the route intents.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800213 *
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800214 * @return the route intents
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800215 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800216 public Collection<MultiPointToSinglePointIntent> getRouteIntents() {
217 List<MultiPointToSinglePointIntent> result = new LinkedList<>();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800218
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800219 for (Map.Entry<IpPrefix, MultiPointToSinglePointIntent> entry :
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800220 routeIntents.entrySet()) {
221 result.add(entry.getValue());
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800222 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800223 return result;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800224 }
225
226 /**
227 * Thread for Intent Synchronization.
228 */
229 private void doIntentSynchronizationThread() {
230 boolean interrupted = false;
231 try {
232 while (!interrupted) {
233 try {
234 intentsSynchronizerSemaphore.acquire();
235 //
236 // Drain all permits, because a single synchronization is
237 // sufficient.
238 //
239 intentsSynchronizerSemaphore.drainPermits();
240 } catch (InterruptedException e) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800241 interrupted = true;
242 break;
243 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800244 synchronizeIntents();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800245 }
246 } finally {
247 if (interrupted) {
248 Thread.currentThread().interrupt();
249 }
250 }
251 }
252
253 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800254 * Submits a collection of point-to-point intents.
255 *
256 * @param intents the intents to submit
257 */
258 void submitPeerIntents(Collection<PointToPointIntent> intents) {
259 synchronized (this) {
260 // Store the intents in memory
261 for (PointToPointIntent intent : intents) {
262 peerIntents.put(new IntentKey(intent), intent);
263 }
264
265 // Push the intents
266 if (isElectedLeader && isActivatedLeader) {
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800267 log.debug("SDN-IP Submitting all Peer Intents...");
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800268 for (Intent intent : intents) {
Brian O'Connor03406a42015-02-03 17:28:57 -0800269 log.trace("SDN-IP Submitting intents: {}", intent);
270 intentService.submit(intent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800271 }
272 }
273 }
274 }
275
276 /**
Pingping Line28ae4c2015-03-13 11:37:03 -0700277 * Submits a MultiPointToSinglePointIntent for reactive routing.
278 *
279 * @param ipPrefix the IP prefix to match in a MultiPointToSinglePointIntent
280 * @param intent the intent to submit
281 */
282 void submitReactiveIntent(IpPrefix ipPrefix, MultiPointToSinglePointIntent intent) {
283 synchronized (this) {
284 // Store the intent in memory
285 routeIntents.put(ipPrefix, intent);
286
287 // Push the intent
288 if (isElectedLeader && isActivatedLeader) {
289 log.trace("SDN-IP submitting reactive routing intent: {}", intent);
290 intentService.submit(intent);
291 }
292 }
293 }
294
295 /**
Jonathan Hart552e31f2015-02-06 11:11:59 -0800296 * Generates a route intent for a prefix, the next hop IP address, and
297 * the next hop MAC address.
298 * <p/>
299 * This method will find the egress interface for the intent.
300 * Intent will match dst IP prefix and rewrite dst MAC address at all other
301 * border switches, then forward packets according to dst MAC address.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800302 *
Jonathan Hart552e31f2015-02-06 11:11:59 -0800303 * @param prefix IP prefix of the route to add
304 * @param nextHopIpAddress IP address of the next hop
305 * @param nextHopMacAddress MAC address of the next hop
306 * @return the generated intent, or null if no intent should be submitted
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800307 */
Jonathan Hart552e31f2015-02-06 11:11:59 -0800308 private MultiPointToSinglePointIntent generateRouteIntent(
309 IpPrefix prefix,
310 IpAddress nextHopIpAddress,
311 MacAddress nextHopMacAddress) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800312
Jonathan Hart552e31f2015-02-06 11:11:59 -0800313 // Find the attachment point (egress interface) of the next hop
Jonathan Hart4cb39882015-08-12 23:50:55 -0400314 Interface egressInterface = interfaceService.getMatchingInterface(nextHopIpAddress);
315 if (egressInterface == null) {
316 log.warn("No outgoing interface found for {}",
317 nextHopIpAddress);
318 return null;
Jonathan Hart552e31f2015-02-06 11:11:59 -0800319 }
320
321 //
322 // Generate the intent itself
323 //
324 Set<ConnectPoint> ingressPorts = new HashSet<>();
325 ConnectPoint egressPort = egressInterface.connectPoint();
326 log.debug("Generating intent for prefix {}, next hop mac {}",
327 prefix, nextHopMacAddress);
328
Jonathan Hart4cb39882015-08-12 23:50:55 -0400329 for (Interface intf : interfaceService.getInterfaces()) {
330 // TODO this should be only peering interfaces
Jonathan Hart552e31f2015-02-06 11:11:59 -0800331 if (!intf.connectPoint().equals(egressInterface.connectPoint())) {
332 ConnectPoint srcPort = intf.connectPoint();
333 ingressPorts.add(srcPort);
334 }
335 }
336
337 // Match the destination IP prefix at the first hop
338 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700339 if (prefix.isIp4()) {
Jonathan Hart552e31f2015-02-06 11:11:59 -0800340 selector.matchEthType(Ethernet.TYPE_IPV4);
Pavlin Radoslavova8537092015-02-23 10:15:20 -0800341 selector.matchIPDst(prefix);
Jonathan Hart552e31f2015-02-06 11:11:59 -0800342 } else {
343 selector.matchEthType(Ethernet.TYPE_IPV6);
Pavlin Radoslavova8537092015-02-23 10:15:20 -0800344 selector.matchIPv6Dst(prefix);
Jonathan Hart552e31f2015-02-06 11:11:59 -0800345 }
Jonathan Hart552e31f2015-02-06 11:11:59 -0800346
347 // Rewrite the destination MAC address
348 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
349 .setEthDst(nextHopMacAddress);
350 if (!egressInterface.vlan().equals(VlanId.NONE)) {
351 treatment.setVlanId(egressInterface.vlan());
352 // If we set VLAN ID, we have to make sure a VLAN tag exists.
353 // TODO support no VLAN -> VLAN routing
354 selector.matchVlanId(VlanId.ANY);
355 }
356
Pavlin Radoslavov2aa1f322015-03-11 17:59:44 -0700357 int priority =
358 prefix.prefixLength() * PRIORITY_MULTIPLIER + PRIORITY_OFFSET;
359 Key key = Key.of(prefix.toString(), appId);
Ray Milkeyebc5d222015-03-18 15:45:36 -0700360 return MultiPointToSinglePointIntent.builder()
361 .appId(appId)
362 .key(key)
363 .selector(selector.build())
364 .treatment(treatment.build())
365 .ingressPoints(ingressPorts)
366 .egressPoint(egressPort)
367 .priority(priority)
Jonathan Hart96c5a4a2015-07-31 14:23:33 -0700368 .constraints(CONSTRAINTS)
Ray Milkeyebc5d222015-03-18 15:45:36 -0700369 .build();
Jonathan Hart552e31f2015-02-06 11:11:59 -0800370 }
371
372 @Override
Pingping Line28ae4c2015-03-13 11:37:03 -0700373 public void setUpConnectivityInternetToHost(IpAddress hostIpAddress) {
374 checkNotNull(hostIpAddress);
Pingping Lin8a524712015-06-24 14:58:24 -0700375 Set<ConnectPoint> ingressPoints =
376 configService.getBgpPeerConnectPoints();
377
Pingping Line28ae4c2015-03-13 11:37:03 -0700378 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
379
380 if (hostIpAddress.isIp4()) {
381 selector.matchEthType(Ethernet.TYPE_IPV4);
382 } else {
383 selector.matchEthType(Ethernet.TYPE_IPV6);
384 }
385
386 // Match the destination IP prefix at the first hop
387 IpPrefix ipPrefix = hostIpAddress.toIpPrefix();
388 selector.matchIPDst(ipPrefix);
389
390 // Rewrite the destination MAC address
391 MacAddress hostMac = null;
392 ConnectPoint egressPoint = null;
393 for (Host host : hostService.getHostsByIp(hostIpAddress)) {
394 if (host.mac() != null) {
395 hostMac = host.mac();
396 egressPoint = host.location();
397 break;
398 }
399 }
400 if (hostMac == null) {
401 hostService.startMonitoringIp(hostIpAddress);
402 return;
403 }
404
405 TrafficTreatment.Builder treatment =
406 DefaultTrafficTreatment.builder().setEthDst(hostMac);
407 Key key = Key.of(ipPrefix.toString(), appId);
408 int priority = ipPrefix.prefixLength() * PRIORITY_MULTIPLIER
409 + PRIORITY_OFFSET;
410 MultiPointToSinglePointIntent intent =
411 MultiPointToSinglePointIntent.builder()
412 .appId(appId)
413 .key(key)
414 .selector(selector.build())
415 .treatment(treatment.build())
416 .ingressPoints(ingressPoints)
417 .egressPoint(egressPoint)
418 .priority(priority)
Jonathan Hart96c5a4a2015-07-31 14:23:33 -0700419 .constraints(CONSTRAINTS)
Pingping Line28ae4c2015-03-13 11:37:03 -0700420 .build();
421
422 log.trace("Generates ConnectivityInternetToHost intent {}", intent);
423 submitReactiveIntent(ipPrefix, intent);
424 }
425
426
427 @Override
Jonathan Hart552e31f2015-02-06 11:11:59 -0800428 public void update(Collection<FibUpdate> updates, Collection<FibUpdate> withdraws) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800429 //
430 // NOTE: Semantically, we MUST withdraw existing intents before
431 // submitting new intents.
432 //
433 synchronized (this) {
434 MultiPointToSinglePointIntent intent;
435
436 log.debug("SDN-IP submitting intents = {} withdrawing = {}",
Jonathan Hart552e31f2015-02-06 11:11:59 -0800437 updates.size(), withdraws.size());
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800438
439 //
440 // Prepare the Intent batch operations for the intents to withdraw
441 //
Jonathan Hart552e31f2015-02-06 11:11:59 -0800442 for (FibUpdate withdraw : withdraws) {
443 checkArgument(withdraw.type() == FibUpdate.Type.DELETE,
444 "FibUpdate with wrong type in withdraws list");
445
446 IpPrefix prefix = withdraw.entry().prefix();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800447 intent = routeIntents.remove(prefix);
448 if (intent == null) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800449 log.trace("SDN-IP No intent in routeIntents to delete " +
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800450 "for prefix: {}", prefix);
451 continue;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800452 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800453 if (isElectedLeader && isActivatedLeader) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800454 log.trace("SDN-IP Withdrawing intent: {}", intent);
Brian O'Connor03406a42015-02-03 17:28:57 -0800455 intentService.withdraw(intent);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800456 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800457 }
458
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800459 //
460 // Prepare the Intent batch operations for the intents to submit
461 //
Jonathan Hart552e31f2015-02-06 11:11:59 -0800462 for (FibUpdate update : updates) {
463 checkArgument(update.type() == FibUpdate.Type.UPDATE,
464 "FibUpdate with wrong type in updates list");
465
466 IpPrefix prefix = update.entry().prefix();
467 intent = generateRouteIntent(prefix, update.entry().nextHopIp(),
468 update.entry().nextHopMac());
469
470 if (intent == null) {
471 // This preserves the old semantics - if an intent can't be
472 // generated, we don't do anything with that prefix. But
473 // perhaps we should withdraw the old intent anyway?
474 continue;
475 }
476
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800477 MultiPointToSinglePointIntent oldIntent =
478 routeIntents.put(prefix, intent);
479 if (isElectedLeader && isActivatedLeader) {
480 if (oldIntent != null) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800481 log.trace("SDN-IP Withdrawing old intent: {}",
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800482 oldIntent);
Brian O'Connor03406a42015-02-03 17:28:57 -0800483 intentService.withdraw(oldIntent);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800484 }
Jonathan Hartb3b8a0a2015-02-12 17:36:13 -0800485 log.trace("SDN-IP Submitting intent: {}", intent);
486 intentService.submit(intent);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800487 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800488 }
489 }
490 }
491
492 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800493 * Synchronize the in-memory Intents with the Intents in the Intent
494 * framework.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800495 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800496 void synchronizeIntents() {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800497 synchronized (this) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800498
499 Map<IntentKey, Intent> localIntents = new HashMap<>();
500 Map<IntentKey, Intent> fetchedIntents = new HashMap<>();
501 Collection<Intent> storeInMemoryIntents = new LinkedList<>();
502 Collection<Intent> addIntents = new LinkedList<>();
503 Collection<Intent> deleteIntents = new LinkedList<>();
504
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800505 if (!isElectedLeader) {
506 return; // Nothing to do: not the leader anymore
507 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800508 log.debug("SDN-IP synchronizing all intents...");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800509
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800510 // Prepare the local intents
511 for (Intent intent : routeIntents.values()) {
512 localIntents.put(new IntentKey(intent), intent);
513 }
514 for (Intent intent : peerIntents.values()) {
515 localIntents.put(new IntentKey(intent), intent);
516 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800517
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800518 // Fetch all intents for this application
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800519 for (Intent intent : intentService.getIntents()) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800520 if (!intent.appId().equals(appId)) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800521 continue;
522 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800523 fetchedIntents.put(new IntentKey(intent), intent);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800524 }
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800525 if (log.isDebugEnabled()) {
526 for (Intent intent: fetchedIntents.values()) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800527 log.trace("SDN-IP Intent Synchronizer: fetched intent: {}",
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800528 intent);
529 }
530 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800531
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800532 computeIntentsDelta(localIntents, fetchedIntents,
533 storeInMemoryIntents, addIntents,
534 deleteIntents);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800535
536 //
537 // Perform the actions:
538 // 1. Store in memory fetched intents that are same. Can be done
539 // even if we are not the leader anymore
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800540 // 2. Delete intents: check if the leader before the operation
541 // 3. Add intents: check if the leader before the operation
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800542 //
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800543 for (Intent intent : storeInMemoryIntents) {
544 // Store the intent in memory based on its type
545 if (intent instanceof MultiPointToSinglePointIntent) {
546 MultiPointToSinglePointIntent mp2pIntent =
547 (MultiPointToSinglePointIntent) intent;
548 // Find the IP prefix
549 Criterion c =
550 mp2pIntent.selector().getCriterion(Criterion.Type.IPV4_DST);
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800551 if (c == null) {
552 // Try IPv6
553 c =
554 mp2pIntent.selector().getCriterion(Criterion.Type.IPV6_DST);
555 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800556 if (c != null && c instanceof IPCriterion) {
557 IPCriterion ipCriterion = (IPCriterion) c;
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800558 IpPrefix ipPrefix = ipCriterion.ip();
559 if (ipPrefix == null) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800560 continue;
561 }
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800562 log.trace("SDN-IP Intent Synchronizer: updating " +
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800563 "in-memory Route Intent for prefix {}",
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800564 ipPrefix);
565 routeIntents.put(ipPrefix, mp2pIntent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800566 } else {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800567 log.warn("SDN-IP no IPV4_DST or IPV6_DST criterion found for Intent {}",
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800568 mp2pIntent.id());
569 }
570 continue;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800571 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800572 if (intent instanceof PointToPointIntent) {
573 PointToPointIntent p2pIntent = (PointToPointIntent) intent;
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800574 log.trace("SDN-IP Intent Synchronizer: updating " +
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800575 "in-memory Peer Intent {}", p2pIntent);
576 peerIntents.put(new IntentKey(intent), p2pIntent);
577 continue;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800578 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800579 }
580
581 // Withdraw Intents
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800582 for (Intent intent : deleteIntents) {
Brian O'Connor03406a42015-02-03 17:28:57 -0800583 intentService.withdraw(intent);
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800584 log.trace("SDN-IP Intent Synchronizer: withdrawing intent: {}",
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800585 intent);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800586 }
587 if (!isElectedLeader) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800588 log.trace("SDN-IP Intent Synchronizer: cannot withdraw intents: " +
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800589 "not elected leader anymore");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800590 isActivatedLeader = false;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800591 return;
592 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800593
594 // Add Intents
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800595 for (Intent intent : addIntents) {
Brian O'Connor03406a42015-02-03 17:28:57 -0800596 intentService.submit(intent);
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800597 log.trace("SDN-IP Intent Synchronizer: submitting intent: {}",
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800598 intent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800599 }
600 if (!isElectedLeader) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800601 log.trace("SDN-IP Intent Synchronizer: cannot submit intents: " +
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800602 "not elected leader anymore");
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800603 isActivatedLeader = false;
604 return;
605 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800606
607 if (isElectedLeader) {
608 isActivatedLeader = true; // Allow push of Intents
609 } else {
610 isActivatedLeader = false;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800611 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800612 log.debug("SDN-IP intent synchronization completed");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800613 }
614 }
615
616 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800617 * Computes the delta in two sets of Intents: local in-memory Intents,
618 * and intents fetched from the Intent framework.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800619 *
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800620 * @param localIntents the local in-memory Intents
621 * @param fetchedIntents the Intents fetched from the Intent framework
622 * @param storeInMemoryIntents the Intents that should be stored in memory.
623 * Note: This Collection must be allocated by the caller, and it will
624 * be populated by this method.
625 * @param addIntents the Intents that should be added to the Intent
626 * framework. Note: This Collection must be allocated by the caller, and
627 * it will be populated by this method.
628 * @param deleteIntents the Intents that should be deleted from the Intent
629 * framework. Note: This Collection must be allocated by the caller, and
630 * it will be populated by this method.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800631 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800632 private void computeIntentsDelta(
633 final Map<IntentKey, Intent> localIntents,
634 final Map<IntentKey, Intent> fetchedIntents,
635 Collection<Intent> storeInMemoryIntents,
636 Collection<Intent> addIntents,
637 Collection<Intent> deleteIntents) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800638
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800639 //
640 // Compute the deltas between the LOCAL in-memory Intents and the
641 // FETCHED Intents:
642 // - If an Intent is in both the LOCAL and FETCHED sets:
643 // If the FETCHED Intent is WITHDRAWING or WITHDRAWN, then
644 // the LOCAL Intent should be added/installed; otherwise the
645 // FETCHED intent should be stored in the local memory
646 // (i.e., override the LOCAL Intent) to preserve the original
647 // Intent ID.
648 // - if a LOCAL Intent is not in the FETCHED set, then the LOCAL
649 // Intent should be added/installed.
650 // - If a FETCHED Intent is not in the LOCAL set, then the FETCHED
651 // Intent should be deleted/withdrawn.
652 //
653 for (Map.Entry<IntentKey, Intent> entry : localIntents.entrySet()) {
654 IntentKey intentKey = entry.getKey();
655 Intent localIntent = entry.getValue();
656 Intent fetchedIntent = fetchedIntents.get(intentKey);
657
658 if (fetchedIntent == null) {
659 //
660 // No FETCHED Intent found: push the LOCAL Intent.
661 //
662 addIntents.add(localIntent);
663 continue;
664 }
665
666 IntentState state =
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800667 intentService.getIntentState(fetchedIntent.key());
Pavlin Radoslavovdeb8a102014-11-26 13:31:36 -0800668 if (state == null ||
669 state == IntentState.WITHDRAWING ||
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800670 state == IntentState.WITHDRAWN) {
671 // The intent has been withdrawn but according to our route
672 // table it should be installed. We'll reinstall it.
673 addIntents.add(localIntent);
674 continue;
675 }
676 storeInMemoryIntents.add(fetchedIntent);
677 }
678
679 for (Map.Entry<IntentKey, Intent> entry : fetchedIntents.entrySet()) {
680 IntentKey intentKey = entry.getKey();
681 Intent fetchedIntent = entry.getValue();
682 Intent localIntent = localIntents.get(intentKey);
683
684 if (localIntent != null) {
685 continue;
686 }
687
688 IntentState state =
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800689 intentService.getIntentState(fetchedIntent.key());
Pavlin Radoslavovdeb8a102014-11-26 13:31:36 -0800690 if (state == null ||
691 state == IntentState.WITHDRAWING ||
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800692 state == IntentState.WITHDRAWN) {
693 // Nothing to do. The intent has been already withdrawn.
694 continue;
695 }
696 //
697 // No LOCAL Intent found: delete/withdraw the FETCHED Intent.
698 //
699 deleteIntents.add(fetchedIntent);
700 }
701 }
702
703 /**
704 * Helper class that can be used to compute the key for an Intent by
705 * by excluding the Intent ID.
706 */
707 static final class IntentKey {
708 private final Intent intent;
709
710 /**
711 * Constructor.
712 *
713 * @param intent the intent to use
714 */
715 IntentKey(Intent intent) {
716 checkArgument((intent instanceof MultiPointToSinglePointIntent) ||
717 (intent instanceof PointToPointIntent),
718 "Intent type not recognized", intent);
719 this.intent = intent;
720 }
721
722 /**
723 * Compares two Multi-Point to Single-Point Intents whether they
724 * represent same logical intention.
725 *
726 * @param intent1 the first Intent to compare
727 * @param intent2 the second Intent to compare
728 * @return true if both Intents represent same logical intention,
729 * otherwise false
730 */
731 static boolean equalIntents(MultiPointToSinglePointIntent intent1,
732 MultiPointToSinglePointIntent intent2) {
733 return Objects.equals(intent1.appId(), intent2.appId()) &&
734 Objects.equals(intent1.selector(), intent2.selector()) &&
735 Objects.equals(intent1.treatment(), intent2.treatment()) &&
736 Objects.equals(intent1.ingressPoints(), intent2.ingressPoints()) &&
737 Objects.equals(intent1.egressPoint(), intent2.egressPoint());
738 }
739
740 /**
741 * Compares two Point-to-Point Intents whether they represent
742 * same logical intention.
743 *
744 * @param intent1 the first Intent to compare
745 * @param intent2 the second Intent to compare
746 * @return true if both Intents represent same logical intention,
747 * otherwise false
748 */
749 static boolean equalIntents(PointToPointIntent intent1,
750 PointToPointIntent intent2) {
751 return Objects.equals(intent1.appId(), intent2.appId()) &&
752 Objects.equals(intent1.selector(), intent2.selector()) &&
753 Objects.equals(intent1.treatment(), intent2.treatment()) &&
754 Objects.equals(intent1.ingressPoint(), intent2.ingressPoint()) &&
755 Objects.equals(intent1.egressPoint(), intent2.egressPoint());
756 }
757
758 @Override
759 public int hashCode() {
760 if (intent instanceof PointToPointIntent) {
761 PointToPointIntent p2pIntent = (PointToPointIntent) intent;
762 return Objects.hash(p2pIntent.appId(),
763 p2pIntent.resources(),
764 p2pIntent.selector(),
765 p2pIntent.treatment(),
766 p2pIntent.constraints(),
767 p2pIntent.ingressPoint(),
768 p2pIntent.egressPoint());
769 }
770 if (intent instanceof MultiPointToSinglePointIntent) {
771 MultiPointToSinglePointIntent m2pIntent =
772 (MultiPointToSinglePointIntent) intent;
773 return Objects.hash(m2pIntent.appId(),
774 m2pIntent.resources(),
775 m2pIntent.selector(),
776 m2pIntent.treatment(),
777 m2pIntent.constraints(),
778 m2pIntent.ingressPoints(),
779 m2pIntent.egressPoint());
780 }
781 checkArgument(false, "Intent type not recognized", intent);
782 return 0;
783 }
784
785 @Override
786 public boolean equals(Object obj) {
787 if (this == obj) {
788 return true;
789 }
790 if ((obj == null) || (!(obj instanceof IntentKey))) {
791 return false;
792 }
793 IntentKey other = (IntentKey) obj;
794
795 if (this.intent instanceof PointToPointIntent) {
796 if (!(other.intent instanceof PointToPointIntent)) {
797 return false;
798 }
799 return equalIntents((PointToPointIntent) this.intent,
800 (PointToPointIntent) other.intent);
801 }
802 if (this.intent instanceof MultiPointToSinglePointIntent) {
803 if (!(other.intent instanceof MultiPointToSinglePointIntent)) {
804 return false;
805 }
806 return equalIntents(
807 (MultiPointToSinglePointIntent) this.intent,
808 (MultiPointToSinglePointIntent) other.intent);
809 }
810 checkArgument(false, "Intent type not recognized", intent);
811 return false;
812 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800813 }
Pingping Line28ae4c2015-03-13 11:37:03 -0700814
815 @Override
816 public void setUpConnectivityHostToHost(IpAddress dstIpAddress,
817 IpAddress srcIpAddress,
818 MacAddress srcMacAddress,
819 ConnectPoint srcConnectPoint) {
820 checkNotNull(dstIpAddress);
821 checkNotNull(srcIpAddress);
822 checkNotNull(srcMacAddress);
823 checkNotNull(srcConnectPoint);
824
825 IpPrefix srcIpPrefix = srcIpAddress.toIpPrefix();
826 IpPrefix dstIpPrefix = dstIpAddress.toIpPrefix();
827 ConnectPoint dstConnectPoint = null;
828 MacAddress dstMacAddress = null;
829
830 for (Host host : hostService.getHostsByIp(dstIpAddress)) {
831 if (host.mac() != null) {
832 dstMacAddress = host.mac();
833 dstConnectPoint = host.location();
834 break;
835 }
836 }
837 if (dstMacAddress == null) {
838 hostService.startMonitoringIp(dstIpAddress);
839 return;
840 }
841
842 //
843 // Handle intent from source host to destination host
844 //
845 MultiPointToSinglePointIntent srcToDstIntent =
846 hostToHostIntentGenerator(dstIpAddress, dstConnectPoint,
847 dstMacAddress, srcConnectPoint);
848 submitReactiveIntent(dstIpPrefix, srcToDstIntent);
849
850 //
851 // Handle intent from destination host to source host
852 //
853
854 // Since we proactively handle the intent from destination host to
855 // source host, we should check whether there is an exiting intent
856 // first.
857 if (mp2pIntentExists(srcIpPrefix)) {
858 updateExistingMp2pIntent(srcIpPrefix, dstConnectPoint);
859 return;
860 } else {
861 // There is no existing intent, create a new one.
862 MultiPointToSinglePointIntent dstToSrcIntent =
863 hostToHostIntentGenerator(srcIpAddress, srcConnectPoint,
864 srcMacAddress, dstConnectPoint);
865 submitReactiveIntent(srcIpPrefix, dstToSrcIntent);
866 }
867 }
868
869 /**
870 * Generates MultiPointToSinglePointIntent for both source host and
871 * destination host located in local SDN network.
872 *
873 * @param dstIpAddress the destination IP address
874 * @param dstConnectPoint the destination host connect point
875 * @param dstMacAddress the MAC address of destination host
876 * @param srcConnectPoint the connect point where packet-in from
877 * @return the generated MultiPointToSinglePointIntent
878 */
879 private MultiPointToSinglePointIntent hostToHostIntentGenerator(
880 IpAddress dstIpAddress,
881 ConnectPoint dstConnectPoint,
882 MacAddress dstMacAddress,
883 ConnectPoint srcConnectPoint) {
884 checkNotNull(dstIpAddress);
885 checkNotNull(dstConnectPoint);
886 checkNotNull(dstMacAddress);
887 checkNotNull(srcConnectPoint);
888
889 Set<ConnectPoint> ingressPoints = new HashSet<ConnectPoint>();
890 ingressPoints.add(srcConnectPoint);
891 IpPrefix dstIpPrefix = dstIpAddress.toIpPrefix();
892
893 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
894 if (dstIpAddress.isIp4()) {
895 selector.matchEthType(Ethernet.TYPE_IPV4);
896 selector.matchIPDst(dstIpPrefix);
897 } else {
898 selector.matchEthType(Ethernet.TYPE_IPV6);
899 selector.matchIPv6Dst(dstIpPrefix);
900 }
901
902 // Rewrite the destination MAC address
903 TrafficTreatment.Builder treatment =
904 DefaultTrafficTreatment.builder().setEthDst(dstMacAddress);
905
906 Key key = Key.of(dstIpPrefix.toString(), appId);
907 int priority = dstIpPrefix.prefixLength() * PRIORITY_MULTIPLIER
908 + PRIORITY_OFFSET;
909 MultiPointToSinglePointIntent intent =
910 MultiPointToSinglePointIntent.builder()
911 .appId(appId)
912 .key(key)
913 .selector(selector.build())
914 .treatment(treatment.build())
915 .ingressPoints(ingressPoints)
916 .egressPoint(dstConnectPoint)
917 .priority(priority)
Jonathan Hart96c5a4a2015-07-31 14:23:33 -0700918 .constraints(CONSTRAINTS)
Pingping Line28ae4c2015-03-13 11:37:03 -0700919 .build();
920
921 log.trace("Generates ConnectivityHostToHost = {} ", intent);
922 return intent;
923 }
924
925 @Override
926 public void updateExistingMp2pIntent(IpPrefix ipPrefix,
927 ConnectPoint ingressConnectPoint) {
928 checkNotNull(ipPrefix);
929 checkNotNull(ingressConnectPoint);
930
931 MultiPointToSinglePointIntent existingIntent =
932 getExistingMp2pIntent(ipPrefix);
933 if (existingIntent != null) {
934 Set<ConnectPoint> ingressPoints = existingIntent.ingressPoints();
935 // Add host connect point into ingressPoints of the existing intent
936 if (ingressPoints.add(ingressConnectPoint)) {
937 MultiPointToSinglePointIntent updatedMp2pIntent =
938 MultiPointToSinglePointIntent.builder()
939 .appId(appId)
940 .key(existingIntent.key())
941 .selector(existingIntent.selector())
942 .treatment(existingIntent.treatment())
943 .ingressPoints(ingressPoints)
944 .egressPoint(existingIntent.egressPoint())
945 .priority(existingIntent.priority())
Jonathan Hart96c5a4a2015-07-31 14:23:33 -0700946 .constraints(CONSTRAINTS)
Pingping Line28ae4c2015-03-13 11:37:03 -0700947 .build();
948
949 log.trace("Update an existing MultiPointToSinglePointIntent "
950 + "to new intent = {} ", updatedMp2pIntent);
951 submitReactiveIntent(ipPrefix, updatedMp2pIntent);
952 }
953 // If adding ingressConnectPoint to ingressPoints failed, it
954 // because between the time interval from checking existing intent
955 // to generating new intent, onos updated this intent due to other
956 // packet-in and the new intent also includes the
957 // ingressConnectPoint. This will not affect reactive routing.
958 }
959 }
960
961 @Override
962 public boolean mp2pIntentExists(IpPrefix ipPrefix) {
963 checkNotNull(ipPrefix);
Sho SHIMIZUfe89d3a2015-06-30 18:15:39 -0700964 return routeIntents.get(ipPrefix) != null;
Pingping Line28ae4c2015-03-13 11:37:03 -0700965 }
966
967 /**
968 * Gets the existing MultiPointToSinglePointIntent from memory for a given
969 * IP prefix.
970 *
971 * @param ipPrefix the IP prefix used to find MultiPointToSinglePointIntent
972 * @return the MultiPointToSinglePointIntent if found, otherwise null
973 */
974 private MultiPointToSinglePointIntent getExistingMp2pIntent(IpPrefix
975 ipPrefix) {
976 checkNotNull(ipPrefix);
977 return routeIntents.get(ipPrefix);
978 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800979}