blob: cde59d1b3e43c80635a22659869f2879758b67d4 [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;
26import org.onosproject.net.ConnectPoint;
27import org.onosproject.net.Host;
28import org.onosproject.net.flow.DefaultTrafficSelector;
29import org.onosproject.net.flow.DefaultTrafficTreatment;
30import org.onosproject.net.flow.TrafficSelector;
31import org.onosproject.net.flow.TrafficTreatment;
32import org.onosproject.net.flow.criteria.Criterion;
33import org.onosproject.net.flow.criteria.IPCriterion;
34import org.onosproject.net.host.HostService;
35import org.onosproject.net.intent.Constraint;
36import org.onosproject.net.intent.Intent;
37import org.onosproject.net.intent.IntentService;
38import org.onosproject.net.intent.IntentState;
39import org.onosproject.net.intent.Key;
40import org.onosproject.net.intent.MultiPointToSinglePointIntent;
41import org.onosproject.net.intent.PointToPointIntent;
42import org.onosproject.net.intent.constraint.PartialFailureConstraint;
43import org.onosproject.routing.FibListener;
44import org.onosproject.routing.FibUpdate;
45import org.onosproject.routing.IntentRequestListener;
46import org.onosproject.routing.config.BgpPeer;
47import org.onosproject.routing.config.Interface;
48import 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;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -080084 private final Map<IntentKey, PointToPointIntent> peerIntents;
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080085 private final Map<IpPrefix, MultiPointToSinglePointIntent> routeIntents;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080086
87 //
88 // State to deal with SDN-IP Leader election and pushing Intents
89 //
90 private final ExecutorService bgpIntentsSynchronizerExecutor;
91 private final Semaphore intentsSynchronizerSemaphore = new Semaphore(0);
92 private volatile boolean isElectedLeader = false;
93 private volatile boolean isActivatedLeader = false;
94
Jonathan Hart90a02c22015-02-13 11:52:07 -080095 private final RoutingConfigurationService configService;
Jonathan Hart552e31f2015-02-06 11:11:59 -080096
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080097 /**
98 * Class constructor.
99 *
100 * @param appId the Application ID
101 * @param intentService the intent service
Pingping Line28ae4c2015-03-13 11:37:03 -0700102 * @param hostService the host service
Jonathan Hart552e31f2015-02-06 11:11:59 -0800103 * @param configService the SDN-IP configuration service
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800104 */
Jonathan Hart552e31f2015-02-06 11:11:59 -0800105 IntentSynchronizer(ApplicationId appId, IntentService intentService,
Pingping Line28ae4c2015-03-13 11:37:03 -0700106 HostService hostService,
Jonathan Hart90a02c22015-02-13 11:52:07 -0800107 RoutingConfigurationService configService) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800108 this.appId = appId;
109 this.intentService = intentService;
Pingping Line28ae4c2015-03-13 11:37:03 -0700110 this.hostService = hostService;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800111 peerIntents = new ConcurrentHashMap<>();
112 routeIntents = new ConcurrentHashMap<>();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800113
Jonathan Hart552e31f2015-02-06 11:11:59 -0800114 this.configService = configService;
Jonathan Hart552e31f2015-02-06 11:11:59 -0800115
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800116 bgpIntentsSynchronizerExecutor = Executors.newSingleThreadExecutor(
117 new ThreadFactoryBuilder()
Pavlin Radoslavov8b752442014-11-18 14:34:37 -0800118 .setNameFormat("sdnip-intents-synchronizer-%d").build());
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800119 }
120
121 /**
122 * Starts the synchronizer.
123 */
124 public void start() {
125 bgpIntentsSynchronizerExecutor.execute(new Runnable() {
126 @Override
127 public void run() {
128 doIntentSynchronizationThread();
129 }
130 });
131 }
132
133 /**
134 * Stops the synchronizer.
135 */
136 public void stop() {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800137 synchronized (this) {
138 // Stop the thread(s)
139 bgpIntentsSynchronizerExecutor.shutdownNow();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800140
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800141 //
142 // Withdraw all SDN-IP intents
143 //
144 if (!isElectedLeader) {
145 return; // Nothing to do: not the leader anymore
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800146 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800147
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800148 //
Pavlin Radoslavov20be3e62014-11-25 18:52:08 -0800149 // NOTE: We don't withdraw the intents during shutdown, because
150 // it creates flux in the data plane during switchover.
151 //
152
153 /*
154 //
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800155 // Build a batch operation to withdraw all intents from this
156 // application.
157 //
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800158 log.debug("SDN-IP Intent Synchronizer shutdown: " +
159 "withdrawing all intents...");
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800160 IntentOperations.Builder builder = IntentOperations.builder(appId);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800161 for (Intent intent : intentService.getIntents()) {
162 // Skip the intents from other applications
163 if (!intent.appId().equals(appId)) {
164 continue;
165 }
166
167 // Skip the intents that are already withdrawn
168 IntentState intentState =
169 intentService.getIntentState(intent.id());
Pavlin Radoslavovdeb8a102014-11-26 13:31:36 -0800170 if ((intentState == null) ||
171 intentState.equals(IntentState.WITHDRAWING) ||
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800172 intentState.equals(IntentState.WITHDRAWN)) {
173 continue;
174 }
175
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800176 log.trace("SDN-IP Intent Synchronizer withdrawing intent: {}",
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800177 intent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800178 builder.addWithdrawOperation(intent.id());
179 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800180 IntentOperations intentOperations = builder.build();
181 intentService.execute(intentOperations);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800182 leaderChanged(false);
183
184 peerIntents.clear();
185 routeIntents.clear();
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800186 log.debug("SDN-IP Intent Synchronizer shutdown completed");
Pavlin Radoslavov20be3e62014-11-25 18:52:08 -0800187 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800188 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800189 }
190
Jonathan Hart51372182014-12-03 21:32:34 -0800191 /**
192 * Signals the synchronizer that the SDN-IP leadership has changed.
193 *
194 * @param isLeader true if this instance is now the leader, otherwise false
195 */
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800196 public void leaderChanged(boolean isLeader) {
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800197 log.debug("SDN-IP Leader changed: {}", isLeader);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800198
199 if (!isLeader) {
200 this.isElectedLeader = false;
201 this.isActivatedLeader = false;
202 return; // Nothing to do
203 }
204 this.isActivatedLeader = false;
205 this.isElectedLeader = true;
206
207 //
208 // Tell the Intents Synchronizer thread to start the synchronization
209 //
210 intentsSynchronizerSemaphore.release();
211 }
212
213 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800214 * Gets the route intents.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800215 *
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800216 * @return the route intents
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800217 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800218 public Collection<MultiPointToSinglePointIntent> getRouteIntents() {
219 List<MultiPointToSinglePointIntent> result = new LinkedList<>();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800220
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800221 for (Map.Entry<IpPrefix, MultiPointToSinglePointIntent> entry :
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800222 routeIntents.entrySet()) {
223 result.add(entry.getValue());
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800224 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800225 return result;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800226 }
227
228 /**
229 * Thread for Intent Synchronization.
230 */
231 private void doIntentSynchronizationThread() {
232 boolean interrupted = false;
233 try {
234 while (!interrupted) {
235 try {
236 intentsSynchronizerSemaphore.acquire();
237 //
238 // Drain all permits, because a single synchronization is
239 // sufficient.
240 //
241 intentsSynchronizerSemaphore.drainPermits();
242 } catch (InterruptedException e) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800243 interrupted = true;
244 break;
245 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800246 synchronizeIntents();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800247 }
248 } finally {
249 if (interrupted) {
250 Thread.currentThread().interrupt();
251 }
252 }
253 }
254
255 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800256 * Submits a collection of point-to-point intents.
257 *
258 * @param intents the intents to submit
259 */
260 void submitPeerIntents(Collection<PointToPointIntent> intents) {
261 synchronized (this) {
262 // Store the intents in memory
263 for (PointToPointIntent intent : intents) {
264 peerIntents.put(new IntentKey(intent), intent);
265 }
266
267 // Push the intents
268 if (isElectedLeader && isActivatedLeader) {
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800269 log.debug("SDN-IP Submitting all Peer Intents...");
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800270 for (Intent intent : intents) {
Brian O'Connor03406a42015-02-03 17:28:57 -0800271 log.trace("SDN-IP Submitting intents: {}", intent);
272 intentService.submit(intent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800273 }
274 }
275 }
276 }
277
278 /**
Pingping Line28ae4c2015-03-13 11:37:03 -0700279 * Submits a MultiPointToSinglePointIntent for reactive routing.
280 *
281 * @param ipPrefix the IP prefix to match in a MultiPointToSinglePointIntent
282 * @param intent the intent to submit
283 */
284 void submitReactiveIntent(IpPrefix ipPrefix, MultiPointToSinglePointIntent intent) {
285 synchronized (this) {
286 // Store the intent in memory
287 routeIntents.put(ipPrefix, intent);
288
289 // Push the intent
290 if (isElectedLeader && isActivatedLeader) {
291 log.trace("SDN-IP submitting reactive routing intent: {}", intent);
292 intentService.submit(intent);
293 }
294 }
295 }
296
297 /**
Jonathan Hart552e31f2015-02-06 11:11:59 -0800298 * Generates a route intent for a prefix, the next hop IP address, and
299 * the next hop MAC address.
300 * <p/>
301 * This method will find the egress interface for the intent.
302 * Intent will match dst IP prefix and rewrite dst MAC address at all other
303 * border switches, then forward packets according to dst MAC address.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800304 *
Jonathan Hart552e31f2015-02-06 11:11:59 -0800305 * @param prefix IP prefix of the route to add
306 * @param nextHopIpAddress IP address of the next hop
307 * @param nextHopMacAddress MAC address of the next hop
308 * @return the generated intent, or null if no intent should be submitted
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800309 */
Jonathan Hart552e31f2015-02-06 11:11:59 -0800310 private MultiPointToSinglePointIntent generateRouteIntent(
311 IpPrefix prefix,
312 IpAddress nextHopIpAddress,
313 MacAddress nextHopMacAddress) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800314
Jonathan Hart552e31f2015-02-06 11:11:59 -0800315 // Find the attachment point (egress interface) of the next hop
316 Interface egressInterface;
317 if (configService.getBgpPeers().containsKey(nextHopIpAddress)) {
318 // Route to a peer
319 log.debug("Route to peer {}", nextHopIpAddress);
320 BgpPeer peer =
321 configService.getBgpPeers().get(nextHopIpAddress);
322 egressInterface =
Jonathan Hart90a02c22015-02-13 11:52:07 -0800323 configService.getInterface(peer.connectPoint());
Jonathan Hart552e31f2015-02-06 11:11:59 -0800324 } else {
325 // Route to non-peer
326 log.debug("Route to non-peer {}", nextHopIpAddress);
327 egressInterface =
Jonathan Hart90a02c22015-02-13 11:52:07 -0800328 configService.getMatchingInterface(nextHopIpAddress);
Jonathan Hart552e31f2015-02-06 11:11:59 -0800329 if (egressInterface == null) {
330 log.warn("No outgoing interface found for {}",
331 nextHopIpAddress);
332 return null;
333 }
334 }
335
336 //
337 // Generate the intent itself
338 //
339 Set<ConnectPoint> ingressPorts = new HashSet<>();
340 ConnectPoint egressPort = egressInterface.connectPoint();
341 log.debug("Generating intent for prefix {}, next hop mac {}",
342 prefix, nextHopMacAddress);
343
Jonathan Hart90a02c22015-02-13 11:52:07 -0800344 for (Interface intf : configService.getInterfaces()) {
Jonathan Hart552e31f2015-02-06 11:11:59 -0800345 if (!intf.connectPoint().equals(egressInterface.connectPoint())) {
346 ConnectPoint srcPort = intf.connectPoint();
347 ingressPorts.add(srcPort);
348 }
349 }
350
351 // Match the destination IP prefix at the first hop
352 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700353 if (prefix.isIp4()) {
Jonathan Hart552e31f2015-02-06 11:11:59 -0800354 selector.matchEthType(Ethernet.TYPE_IPV4);
Pavlin Radoslavova8537092015-02-23 10:15:20 -0800355 selector.matchIPDst(prefix);
Jonathan Hart552e31f2015-02-06 11:11:59 -0800356 } else {
357 selector.matchEthType(Ethernet.TYPE_IPV6);
Pavlin Radoslavova8537092015-02-23 10:15:20 -0800358 selector.matchIPv6Dst(prefix);
Jonathan Hart552e31f2015-02-06 11:11:59 -0800359 }
Jonathan Hart552e31f2015-02-06 11:11:59 -0800360
361 // Rewrite the destination MAC address
362 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
363 .setEthDst(nextHopMacAddress);
364 if (!egressInterface.vlan().equals(VlanId.NONE)) {
365 treatment.setVlanId(egressInterface.vlan());
366 // If we set VLAN ID, we have to make sure a VLAN tag exists.
367 // TODO support no VLAN -> VLAN routing
368 selector.matchVlanId(VlanId.ANY);
369 }
370
Pavlin Radoslavov2aa1f322015-03-11 17:59:44 -0700371 int priority =
372 prefix.prefixLength() * PRIORITY_MULTIPLIER + PRIORITY_OFFSET;
373 Key key = Key.of(prefix.toString(), appId);
Ray Milkeyebc5d222015-03-18 15:45:36 -0700374 return MultiPointToSinglePointIntent.builder()
375 .appId(appId)
376 .key(key)
377 .selector(selector.build())
378 .treatment(treatment.build())
379 .ingressPoints(ingressPorts)
380 .egressPoint(egressPort)
381 .priority(priority)
Jonathan Hart96c5a4a2015-07-31 14:23:33 -0700382 .constraints(CONSTRAINTS)
Ray Milkeyebc5d222015-03-18 15:45:36 -0700383 .build();
Jonathan Hart552e31f2015-02-06 11:11:59 -0800384 }
385
386 @Override
Pingping Line28ae4c2015-03-13 11:37:03 -0700387 public void setUpConnectivityInternetToHost(IpAddress hostIpAddress) {
388 checkNotNull(hostIpAddress);
Pingping Lin8a524712015-06-24 14:58:24 -0700389 Set<ConnectPoint> ingressPoints =
390 configService.getBgpPeerConnectPoints();
391
Pingping Line28ae4c2015-03-13 11:37:03 -0700392 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
393
394 if (hostIpAddress.isIp4()) {
395 selector.matchEthType(Ethernet.TYPE_IPV4);
396 } else {
397 selector.matchEthType(Ethernet.TYPE_IPV6);
398 }
399
400 // Match the destination IP prefix at the first hop
401 IpPrefix ipPrefix = hostIpAddress.toIpPrefix();
402 selector.matchIPDst(ipPrefix);
403
404 // Rewrite the destination MAC address
405 MacAddress hostMac = null;
406 ConnectPoint egressPoint = null;
407 for (Host host : hostService.getHostsByIp(hostIpAddress)) {
408 if (host.mac() != null) {
409 hostMac = host.mac();
410 egressPoint = host.location();
411 break;
412 }
413 }
414 if (hostMac == null) {
415 hostService.startMonitoringIp(hostIpAddress);
416 return;
417 }
418
419 TrafficTreatment.Builder treatment =
420 DefaultTrafficTreatment.builder().setEthDst(hostMac);
421 Key key = Key.of(ipPrefix.toString(), appId);
422 int priority = ipPrefix.prefixLength() * PRIORITY_MULTIPLIER
423 + PRIORITY_OFFSET;
424 MultiPointToSinglePointIntent intent =
425 MultiPointToSinglePointIntent.builder()
426 .appId(appId)
427 .key(key)
428 .selector(selector.build())
429 .treatment(treatment.build())
430 .ingressPoints(ingressPoints)
431 .egressPoint(egressPoint)
432 .priority(priority)
Jonathan Hart96c5a4a2015-07-31 14:23:33 -0700433 .constraints(CONSTRAINTS)
Pingping Line28ae4c2015-03-13 11:37:03 -0700434 .build();
435
436 log.trace("Generates ConnectivityInternetToHost intent {}", intent);
437 submitReactiveIntent(ipPrefix, intent);
438 }
439
440
441 @Override
Jonathan Hart552e31f2015-02-06 11:11:59 -0800442 public void update(Collection<FibUpdate> updates, Collection<FibUpdate> withdraws) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800443 //
444 // NOTE: Semantically, we MUST withdraw existing intents before
445 // submitting new intents.
446 //
447 synchronized (this) {
448 MultiPointToSinglePointIntent intent;
449
450 log.debug("SDN-IP submitting intents = {} withdrawing = {}",
Jonathan Hart552e31f2015-02-06 11:11:59 -0800451 updates.size(), withdraws.size());
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800452
453 //
454 // Prepare the Intent batch operations for the intents to withdraw
455 //
Jonathan Hart552e31f2015-02-06 11:11:59 -0800456 for (FibUpdate withdraw : withdraws) {
457 checkArgument(withdraw.type() == FibUpdate.Type.DELETE,
458 "FibUpdate with wrong type in withdraws list");
459
460 IpPrefix prefix = withdraw.entry().prefix();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800461 intent = routeIntents.remove(prefix);
462 if (intent == null) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800463 log.trace("SDN-IP No intent in routeIntents to delete " +
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800464 "for prefix: {}", prefix);
465 continue;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800466 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800467 if (isElectedLeader && isActivatedLeader) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800468 log.trace("SDN-IP Withdrawing intent: {}", intent);
Brian O'Connor03406a42015-02-03 17:28:57 -0800469 intentService.withdraw(intent);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800470 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800471 }
472
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800473 //
474 // Prepare the Intent batch operations for the intents to submit
475 //
Jonathan Hart552e31f2015-02-06 11:11:59 -0800476 for (FibUpdate update : updates) {
477 checkArgument(update.type() == FibUpdate.Type.UPDATE,
478 "FibUpdate with wrong type in updates list");
479
480 IpPrefix prefix = update.entry().prefix();
481 intent = generateRouteIntent(prefix, update.entry().nextHopIp(),
482 update.entry().nextHopMac());
483
484 if (intent == null) {
485 // This preserves the old semantics - if an intent can't be
486 // generated, we don't do anything with that prefix. But
487 // perhaps we should withdraw the old intent anyway?
488 continue;
489 }
490
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800491 MultiPointToSinglePointIntent oldIntent =
492 routeIntents.put(prefix, intent);
493 if (isElectedLeader && isActivatedLeader) {
494 if (oldIntent != null) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800495 log.trace("SDN-IP Withdrawing old intent: {}",
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800496 oldIntent);
Brian O'Connor03406a42015-02-03 17:28:57 -0800497 intentService.withdraw(oldIntent);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800498 }
Jonathan Hartb3b8a0a2015-02-12 17:36:13 -0800499 log.trace("SDN-IP Submitting intent: {}", intent);
500 intentService.submit(intent);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800501 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800502 }
503 }
504 }
505
506 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800507 * Synchronize the in-memory Intents with the Intents in the Intent
508 * framework.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800509 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800510 void synchronizeIntents() {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800511 synchronized (this) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800512
513 Map<IntentKey, Intent> localIntents = new HashMap<>();
514 Map<IntentKey, Intent> fetchedIntents = new HashMap<>();
515 Collection<Intent> storeInMemoryIntents = new LinkedList<>();
516 Collection<Intent> addIntents = new LinkedList<>();
517 Collection<Intent> deleteIntents = new LinkedList<>();
518
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800519 if (!isElectedLeader) {
520 return; // Nothing to do: not the leader anymore
521 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800522 log.debug("SDN-IP synchronizing all intents...");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800523
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800524 // Prepare the local intents
525 for (Intent intent : routeIntents.values()) {
526 localIntents.put(new IntentKey(intent), intent);
527 }
528 for (Intent intent : peerIntents.values()) {
529 localIntents.put(new IntentKey(intent), intent);
530 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800531
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800532 // Fetch all intents for this application
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800533 for (Intent intent : intentService.getIntents()) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800534 if (!intent.appId().equals(appId)) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800535 continue;
536 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800537 fetchedIntents.put(new IntentKey(intent), intent);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800538 }
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800539 if (log.isDebugEnabled()) {
540 for (Intent intent: fetchedIntents.values()) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800541 log.trace("SDN-IP Intent Synchronizer: fetched intent: {}",
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800542 intent);
543 }
544 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800545
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800546 computeIntentsDelta(localIntents, fetchedIntents,
547 storeInMemoryIntents, addIntents,
548 deleteIntents);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800549
550 //
551 // Perform the actions:
552 // 1. Store in memory fetched intents that are same. Can be done
553 // even if we are not the leader anymore
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800554 // 2. Delete intents: check if the leader before the operation
555 // 3. Add intents: check if the leader before the operation
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800556 //
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800557 for (Intent intent : storeInMemoryIntents) {
558 // Store the intent in memory based on its type
559 if (intent instanceof MultiPointToSinglePointIntent) {
560 MultiPointToSinglePointIntent mp2pIntent =
561 (MultiPointToSinglePointIntent) intent;
562 // Find the IP prefix
563 Criterion c =
564 mp2pIntent.selector().getCriterion(Criterion.Type.IPV4_DST);
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800565 if (c == null) {
566 // Try IPv6
567 c =
568 mp2pIntent.selector().getCriterion(Criterion.Type.IPV6_DST);
569 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800570 if (c != null && c instanceof IPCriterion) {
571 IPCriterion ipCriterion = (IPCriterion) c;
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800572 IpPrefix ipPrefix = ipCriterion.ip();
573 if (ipPrefix == null) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800574 continue;
575 }
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800576 log.trace("SDN-IP Intent Synchronizer: updating " +
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800577 "in-memory Route Intent for prefix {}",
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800578 ipPrefix);
579 routeIntents.put(ipPrefix, mp2pIntent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800580 } else {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800581 log.warn("SDN-IP no IPV4_DST or IPV6_DST criterion found for Intent {}",
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800582 mp2pIntent.id());
583 }
584 continue;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800585 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800586 if (intent instanceof PointToPointIntent) {
587 PointToPointIntent p2pIntent = (PointToPointIntent) intent;
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800588 log.trace("SDN-IP Intent Synchronizer: updating " +
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800589 "in-memory Peer Intent {}", p2pIntent);
590 peerIntents.put(new IntentKey(intent), p2pIntent);
591 continue;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800592 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800593 }
594
595 // Withdraw Intents
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800596 for (Intent intent : deleteIntents) {
Brian O'Connor03406a42015-02-03 17:28:57 -0800597 intentService.withdraw(intent);
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800598 log.trace("SDN-IP Intent Synchronizer: withdrawing intent: {}",
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800599 intent);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800600 }
601 if (!isElectedLeader) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800602 log.trace("SDN-IP Intent Synchronizer: cannot withdraw intents: " +
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800603 "not elected leader anymore");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800604 isActivatedLeader = false;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800605 return;
606 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800607
608 // Add Intents
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800609 for (Intent intent : addIntents) {
Brian O'Connor03406a42015-02-03 17:28:57 -0800610 intentService.submit(intent);
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800611 log.trace("SDN-IP Intent Synchronizer: submitting intent: {}",
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800612 intent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800613 }
614 if (!isElectedLeader) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800615 log.trace("SDN-IP Intent Synchronizer: cannot submit intents: " +
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800616 "not elected leader anymore");
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800617 isActivatedLeader = false;
618 return;
619 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800620
621 if (isElectedLeader) {
622 isActivatedLeader = true; // Allow push of Intents
623 } else {
624 isActivatedLeader = false;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800625 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800626 log.debug("SDN-IP intent synchronization completed");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800627 }
628 }
629
630 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800631 * Computes the delta in two sets of Intents: local in-memory Intents,
632 * and intents fetched from the Intent framework.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800633 *
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800634 * @param localIntents the local in-memory Intents
635 * @param fetchedIntents the Intents fetched from the Intent framework
636 * @param storeInMemoryIntents the Intents that should be stored in memory.
637 * Note: This Collection must be allocated by the caller, and it will
638 * be populated by this method.
639 * @param addIntents the Intents that should be added to the Intent
640 * framework. Note: This Collection must be allocated by the caller, and
641 * it will be populated by this method.
642 * @param deleteIntents the Intents that should be deleted from the Intent
643 * framework. Note: This Collection must be allocated by the caller, and
644 * it will be populated by this method.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800645 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800646 private void computeIntentsDelta(
647 final Map<IntentKey, Intent> localIntents,
648 final Map<IntentKey, Intent> fetchedIntents,
649 Collection<Intent> storeInMemoryIntents,
650 Collection<Intent> addIntents,
651 Collection<Intent> deleteIntents) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800652
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800653 //
654 // Compute the deltas between the LOCAL in-memory Intents and the
655 // FETCHED Intents:
656 // - If an Intent is in both the LOCAL and FETCHED sets:
657 // If the FETCHED Intent is WITHDRAWING or WITHDRAWN, then
658 // the LOCAL Intent should be added/installed; otherwise the
659 // FETCHED intent should be stored in the local memory
660 // (i.e., override the LOCAL Intent) to preserve the original
661 // Intent ID.
662 // - if a LOCAL Intent is not in the FETCHED set, then the LOCAL
663 // Intent should be added/installed.
664 // - If a FETCHED Intent is not in the LOCAL set, then the FETCHED
665 // Intent should be deleted/withdrawn.
666 //
667 for (Map.Entry<IntentKey, Intent> entry : localIntents.entrySet()) {
668 IntentKey intentKey = entry.getKey();
669 Intent localIntent = entry.getValue();
670 Intent fetchedIntent = fetchedIntents.get(intentKey);
671
672 if (fetchedIntent == null) {
673 //
674 // No FETCHED Intent found: push the LOCAL Intent.
675 //
676 addIntents.add(localIntent);
677 continue;
678 }
679
680 IntentState state =
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800681 intentService.getIntentState(fetchedIntent.key());
Pavlin Radoslavovdeb8a102014-11-26 13:31:36 -0800682 if (state == null ||
683 state == IntentState.WITHDRAWING ||
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800684 state == IntentState.WITHDRAWN) {
685 // The intent has been withdrawn but according to our route
686 // table it should be installed. We'll reinstall it.
687 addIntents.add(localIntent);
688 continue;
689 }
690 storeInMemoryIntents.add(fetchedIntent);
691 }
692
693 for (Map.Entry<IntentKey, Intent> entry : fetchedIntents.entrySet()) {
694 IntentKey intentKey = entry.getKey();
695 Intent fetchedIntent = entry.getValue();
696 Intent localIntent = localIntents.get(intentKey);
697
698 if (localIntent != null) {
699 continue;
700 }
701
702 IntentState state =
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800703 intentService.getIntentState(fetchedIntent.key());
Pavlin Radoslavovdeb8a102014-11-26 13:31:36 -0800704 if (state == null ||
705 state == IntentState.WITHDRAWING ||
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800706 state == IntentState.WITHDRAWN) {
707 // Nothing to do. The intent has been already withdrawn.
708 continue;
709 }
710 //
711 // No LOCAL Intent found: delete/withdraw the FETCHED Intent.
712 //
713 deleteIntents.add(fetchedIntent);
714 }
715 }
716
717 /**
718 * Helper class that can be used to compute the key for an Intent by
719 * by excluding the Intent ID.
720 */
721 static final class IntentKey {
722 private final Intent intent;
723
724 /**
725 * Constructor.
726 *
727 * @param intent the intent to use
728 */
729 IntentKey(Intent intent) {
730 checkArgument((intent instanceof MultiPointToSinglePointIntent) ||
731 (intent instanceof PointToPointIntent),
732 "Intent type not recognized", intent);
733 this.intent = intent;
734 }
735
736 /**
737 * Compares two Multi-Point to Single-Point Intents whether they
738 * represent same logical intention.
739 *
740 * @param intent1 the first Intent to compare
741 * @param intent2 the second Intent to compare
742 * @return true if both Intents represent same logical intention,
743 * otherwise false
744 */
745 static boolean equalIntents(MultiPointToSinglePointIntent intent1,
746 MultiPointToSinglePointIntent intent2) {
747 return Objects.equals(intent1.appId(), intent2.appId()) &&
748 Objects.equals(intent1.selector(), intent2.selector()) &&
749 Objects.equals(intent1.treatment(), intent2.treatment()) &&
750 Objects.equals(intent1.ingressPoints(), intent2.ingressPoints()) &&
751 Objects.equals(intent1.egressPoint(), intent2.egressPoint());
752 }
753
754 /**
755 * Compares two Point-to-Point Intents whether they represent
756 * same logical intention.
757 *
758 * @param intent1 the first Intent to compare
759 * @param intent2 the second Intent to compare
760 * @return true if both Intents represent same logical intention,
761 * otherwise false
762 */
763 static boolean equalIntents(PointToPointIntent intent1,
764 PointToPointIntent intent2) {
765 return Objects.equals(intent1.appId(), intent2.appId()) &&
766 Objects.equals(intent1.selector(), intent2.selector()) &&
767 Objects.equals(intent1.treatment(), intent2.treatment()) &&
768 Objects.equals(intent1.ingressPoint(), intent2.ingressPoint()) &&
769 Objects.equals(intent1.egressPoint(), intent2.egressPoint());
770 }
771
772 @Override
773 public int hashCode() {
774 if (intent instanceof PointToPointIntent) {
775 PointToPointIntent p2pIntent = (PointToPointIntent) intent;
776 return Objects.hash(p2pIntent.appId(),
777 p2pIntent.resources(),
778 p2pIntent.selector(),
779 p2pIntent.treatment(),
780 p2pIntent.constraints(),
781 p2pIntent.ingressPoint(),
782 p2pIntent.egressPoint());
783 }
784 if (intent instanceof MultiPointToSinglePointIntent) {
785 MultiPointToSinglePointIntent m2pIntent =
786 (MultiPointToSinglePointIntent) intent;
787 return Objects.hash(m2pIntent.appId(),
788 m2pIntent.resources(),
789 m2pIntent.selector(),
790 m2pIntent.treatment(),
791 m2pIntent.constraints(),
792 m2pIntent.ingressPoints(),
793 m2pIntent.egressPoint());
794 }
795 checkArgument(false, "Intent type not recognized", intent);
796 return 0;
797 }
798
799 @Override
800 public boolean equals(Object obj) {
801 if (this == obj) {
802 return true;
803 }
804 if ((obj == null) || (!(obj instanceof IntentKey))) {
805 return false;
806 }
807 IntentKey other = (IntentKey) obj;
808
809 if (this.intent instanceof PointToPointIntent) {
810 if (!(other.intent instanceof PointToPointIntent)) {
811 return false;
812 }
813 return equalIntents((PointToPointIntent) this.intent,
814 (PointToPointIntent) other.intent);
815 }
816 if (this.intent instanceof MultiPointToSinglePointIntent) {
817 if (!(other.intent instanceof MultiPointToSinglePointIntent)) {
818 return false;
819 }
820 return equalIntents(
821 (MultiPointToSinglePointIntent) this.intent,
822 (MultiPointToSinglePointIntent) other.intent);
823 }
824 checkArgument(false, "Intent type not recognized", intent);
825 return false;
826 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800827 }
Pingping Line28ae4c2015-03-13 11:37:03 -0700828
829 @Override
830 public void setUpConnectivityHostToHost(IpAddress dstIpAddress,
831 IpAddress srcIpAddress,
832 MacAddress srcMacAddress,
833 ConnectPoint srcConnectPoint) {
834 checkNotNull(dstIpAddress);
835 checkNotNull(srcIpAddress);
836 checkNotNull(srcMacAddress);
837 checkNotNull(srcConnectPoint);
838
839 IpPrefix srcIpPrefix = srcIpAddress.toIpPrefix();
840 IpPrefix dstIpPrefix = dstIpAddress.toIpPrefix();
841 ConnectPoint dstConnectPoint = null;
842 MacAddress dstMacAddress = null;
843
844 for (Host host : hostService.getHostsByIp(dstIpAddress)) {
845 if (host.mac() != null) {
846 dstMacAddress = host.mac();
847 dstConnectPoint = host.location();
848 break;
849 }
850 }
851 if (dstMacAddress == null) {
852 hostService.startMonitoringIp(dstIpAddress);
853 return;
854 }
855
856 //
857 // Handle intent from source host to destination host
858 //
859 MultiPointToSinglePointIntent srcToDstIntent =
860 hostToHostIntentGenerator(dstIpAddress, dstConnectPoint,
861 dstMacAddress, srcConnectPoint);
862 submitReactiveIntent(dstIpPrefix, srcToDstIntent);
863
864 //
865 // Handle intent from destination host to source host
866 //
867
868 // Since we proactively handle the intent from destination host to
869 // source host, we should check whether there is an exiting intent
870 // first.
871 if (mp2pIntentExists(srcIpPrefix)) {
872 updateExistingMp2pIntent(srcIpPrefix, dstConnectPoint);
873 return;
874 } else {
875 // There is no existing intent, create a new one.
876 MultiPointToSinglePointIntent dstToSrcIntent =
877 hostToHostIntentGenerator(srcIpAddress, srcConnectPoint,
878 srcMacAddress, dstConnectPoint);
879 submitReactiveIntent(srcIpPrefix, dstToSrcIntent);
880 }
881 }
882
883 /**
884 * Generates MultiPointToSinglePointIntent for both source host and
885 * destination host located in local SDN network.
886 *
887 * @param dstIpAddress the destination IP address
888 * @param dstConnectPoint the destination host connect point
889 * @param dstMacAddress the MAC address of destination host
890 * @param srcConnectPoint the connect point where packet-in from
891 * @return the generated MultiPointToSinglePointIntent
892 */
893 private MultiPointToSinglePointIntent hostToHostIntentGenerator(
894 IpAddress dstIpAddress,
895 ConnectPoint dstConnectPoint,
896 MacAddress dstMacAddress,
897 ConnectPoint srcConnectPoint) {
898 checkNotNull(dstIpAddress);
899 checkNotNull(dstConnectPoint);
900 checkNotNull(dstMacAddress);
901 checkNotNull(srcConnectPoint);
902
903 Set<ConnectPoint> ingressPoints = new HashSet<ConnectPoint>();
904 ingressPoints.add(srcConnectPoint);
905 IpPrefix dstIpPrefix = dstIpAddress.toIpPrefix();
906
907 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
908 if (dstIpAddress.isIp4()) {
909 selector.matchEthType(Ethernet.TYPE_IPV4);
910 selector.matchIPDst(dstIpPrefix);
911 } else {
912 selector.matchEthType(Ethernet.TYPE_IPV6);
913 selector.matchIPv6Dst(dstIpPrefix);
914 }
915
916 // Rewrite the destination MAC address
917 TrafficTreatment.Builder treatment =
918 DefaultTrafficTreatment.builder().setEthDst(dstMacAddress);
919
920 Key key = Key.of(dstIpPrefix.toString(), appId);
921 int priority = dstIpPrefix.prefixLength() * PRIORITY_MULTIPLIER
922 + PRIORITY_OFFSET;
923 MultiPointToSinglePointIntent intent =
924 MultiPointToSinglePointIntent.builder()
925 .appId(appId)
926 .key(key)
927 .selector(selector.build())
928 .treatment(treatment.build())
929 .ingressPoints(ingressPoints)
930 .egressPoint(dstConnectPoint)
931 .priority(priority)
Jonathan Hart96c5a4a2015-07-31 14:23:33 -0700932 .constraints(CONSTRAINTS)
Pingping Line28ae4c2015-03-13 11:37:03 -0700933 .build();
934
935 log.trace("Generates ConnectivityHostToHost = {} ", intent);
936 return intent;
937 }
938
939 @Override
940 public void updateExistingMp2pIntent(IpPrefix ipPrefix,
941 ConnectPoint ingressConnectPoint) {
942 checkNotNull(ipPrefix);
943 checkNotNull(ingressConnectPoint);
944
945 MultiPointToSinglePointIntent existingIntent =
946 getExistingMp2pIntent(ipPrefix);
947 if (existingIntent != null) {
948 Set<ConnectPoint> ingressPoints = existingIntent.ingressPoints();
949 // Add host connect point into ingressPoints of the existing intent
950 if (ingressPoints.add(ingressConnectPoint)) {
951 MultiPointToSinglePointIntent updatedMp2pIntent =
952 MultiPointToSinglePointIntent.builder()
953 .appId(appId)
954 .key(existingIntent.key())
955 .selector(existingIntent.selector())
956 .treatment(existingIntent.treatment())
957 .ingressPoints(ingressPoints)
958 .egressPoint(existingIntent.egressPoint())
959 .priority(existingIntent.priority())
Jonathan Hart96c5a4a2015-07-31 14:23:33 -0700960 .constraints(CONSTRAINTS)
Pingping Line28ae4c2015-03-13 11:37:03 -0700961 .build();
962
963 log.trace("Update an existing MultiPointToSinglePointIntent "
964 + "to new intent = {} ", updatedMp2pIntent);
965 submitReactiveIntent(ipPrefix, updatedMp2pIntent);
966 }
967 // If adding ingressConnectPoint to ingressPoints failed, it
968 // because between the time interval from checking existing intent
969 // to generating new intent, onos updated this intent due to other
970 // packet-in and the new intent also includes the
971 // ingressConnectPoint. This will not affect reactive routing.
972 }
973 }
974
975 @Override
976 public boolean mp2pIntentExists(IpPrefix ipPrefix) {
977 checkNotNull(ipPrefix);
Sho SHIMIZUfe89d3a2015-06-30 18:15:39 -0700978 return routeIntents.get(ipPrefix) != null;
Pingping Line28ae4c2015-03-13 11:37:03 -0700979 }
980
981 /**
982 * Gets the existing MultiPointToSinglePointIntent from memory for a given
983 * IP prefix.
984 *
985 * @param ipPrefix the IP prefix used to find MultiPointToSinglePointIntent
986 * @return the MultiPointToSinglePointIntent if found, otherwise null
987 */
988 private MultiPointToSinglePointIntent getExistingMp2pIntent(IpPrefix
989 ipPrefix) {
990 checkNotNull(ipPrefix);
991 return routeIntents.get(ipPrefix);
992 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800993}