blob: 35ff70540b380f744059c4820bff29300898a41f [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
Ray Milkey9b36d812015-09-09 15:24:54 -0700105 * @param interfaceService the interface service
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800106 */
Jonathan Hart552e31f2015-02-06 11:11:59 -0800107 IntentSynchronizer(ApplicationId appId, IntentService intentService,
Pingping Line28ae4c2015-03-13 11:37:03 -0700108 HostService hostService,
Jonathan Hart4cb39882015-08-12 23:50:55 -0400109 RoutingConfigurationService configService,
110 InterfaceService interfaceService) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800111 this.appId = appId;
112 this.intentService = intentService;
Pingping Line28ae4c2015-03-13 11:37:03 -0700113 this.hostService = hostService;
Jonathan Hart4cb39882015-08-12 23:50:55 -0400114 this.interfaceService = interfaceService;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800115 peerIntents = new ConcurrentHashMap<>();
116 routeIntents = new ConcurrentHashMap<>();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800117
Jonathan Hart552e31f2015-02-06 11:11:59 -0800118 this.configService = configService;
Jonathan Hart552e31f2015-02-06 11:11:59 -0800119
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800120 bgpIntentsSynchronizerExecutor = Executors.newSingleThreadExecutor(
121 new ThreadFactoryBuilder()
Pavlin Radoslavov8b752442014-11-18 14:34:37 -0800122 .setNameFormat("sdnip-intents-synchronizer-%d").build());
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800123 }
124
125 /**
126 * Starts the synchronizer.
127 */
128 public void start() {
Jonathan Hart4cb39882015-08-12 23:50:55 -0400129 bgpIntentsSynchronizerExecutor.execute(this::doIntentSynchronizationThread);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800130 }
131
132 /**
133 * Stops the synchronizer.
134 */
135 public void stop() {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800136 synchronized (this) {
137 // Stop the thread(s)
138 bgpIntentsSynchronizerExecutor.shutdownNow();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800139
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800140 //
141 // Withdraw all SDN-IP intents
142 //
143 if (!isElectedLeader) {
144 return; // Nothing to do: not the leader anymore
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800145 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800146
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800147 //
Pavlin Radoslavov20be3e62014-11-25 18:52:08 -0800148 // NOTE: We don't withdraw the intents during shutdown, because
149 // it creates flux in the data plane during switchover.
150 //
151
152 /*
153 //
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800154 // Build a batch operation to withdraw all intents from this
155 // application.
156 //
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800157 log.debug("SDN-IP Intent Synchronizer shutdown: " +
158 "withdrawing all intents...");
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800159 IntentOperations.Builder builder = IntentOperations.builder(appId);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800160 for (Intent intent : intentService.getIntents()) {
161 // Skip the intents from other applications
162 if (!intent.appId().equals(appId)) {
163 continue;
164 }
165
166 // Skip the intents that are already withdrawn
167 IntentState intentState =
168 intentService.getIntentState(intent.id());
Pavlin Radoslavovdeb8a102014-11-26 13:31:36 -0800169 if ((intentState == null) ||
170 intentState.equals(IntentState.WITHDRAWING) ||
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800171 intentState.equals(IntentState.WITHDRAWN)) {
172 continue;
173 }
174
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800175 log.trace("SDN-IP Intent Synchronizer withdrawing intent: {}",
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800176 intent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800177 builder.addWithdrawOperation(intent.id());
178 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800179 IntentOperations intentOperations = builder.build();
180 intentService.execute(intentOperations);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800181 leaderChanged(false);
182
183 peerIntents.clear();
184 routeIntents.clear();
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800185 log.debug("SDN-IP Intent Synchronizer shutdown completed");
Pavlin Radoslavov20be3e62014-11-25 18:52:08 -0800186 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800187 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800188 }
189
Jonathan Hart51372182014-12-03 21:32:34 -0800190 /**
191 * Signals the synchronizer that the SDN-IP leadership has changed.
192 *
193 * @param isLeader true if this instance is now the leader, otherwise false
194 */
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800195 public void leaderChanged(boolean isLeader) {
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800196 log.debug("SDN-IP Leader changed: {}", isLeader);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800197
198 if (!isLeader) {
199 this.isElectedLeader = false;
200 this.isActivatedLeader = false;
201 return; // Nothing to do
202 }
203 this.isActivatedLeader = false;
204 this.isElectedLeader = true;
205
206 //
207 // Tell the Intents Synchronizer thread to start the synchronization
208 //
209 intentsSynchronizerSemaphore.release();
210 }
211
212 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800213 * Gets the route intents.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800214 *
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800215 * @return the route intents
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800216 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800217 public Collection<MultiPointToSinglePointIntent> getRouteIntents() {
218 List<MultiPointToSinglePointIntent> result = new LinkedList<>();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800219
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800220 for (Map.Entry<IpPrefix, MultiPointToSinglePointIntent> entry :
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800221 routeIntents.entrySet()) {
222 result.add(entry.getValue());
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800223 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800224 return result;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800225 }
226
227 /**
228 * Thread for Intent Synchronization.
229 */
230 private void doIntentSynchronizationThread() {
231 boolean interrupted = false;
232 try {
233 while (!interrupted) {
234 try {
235 intentsSynchronizerSemaphore.acquire();
236 //
237 // Drain all permits, because a single synchronization is
238 // sufficient.
239 //
240 intentsSynchronizerSemaphore.drainPermits();
241 } catch (InterruptedException e) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800242 interrupted = true;
243 break;
244 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800245 synchronizeIntents();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800246 }
247 } finally {
248 if (interrupted) {
249 Thread.currentThread().interrupt();
250 }
251 }
252 }
253
254 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800255 * Submits a collection of point-to-point intents.
256 *
257 * @param intents the intents to submit
258 */
259 void submitPeerIntents(Collection<PointToPointIntent> intents) {
260 synchronized (this) {
261 // Store the intents in memory
262 for (PointToPointIntent intent : intents) {
263 peerIntents.put(new IntentKey(intent), intent);
264 }
265
266 // Push the intents
267 if (isElectedLeader && isActivatedLeader) {
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800268 log.debug("SDN-IP Submitting all Peer Intents...");
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800269 for (Intent intent : intents) {
Brian O'Connor03406a42015-02-03 17:28:57 -0800270 log.trace("SDN-IP Submitting intents: {}", intent);
271 intentService.submit(intent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800272 }
273 }
274 }
275 }
276
277 /**
Pingping Line28ae4c2015-03-13 11:37:03 -0700278 * Submits a MultiPointToSinglePointIntent for reactive routing.
279 *
280 * @param ipPrefix the IP prefix to match in a MultiPointToSinglePointIntent
281 * @param intent the intent to submit
282 */
283 void submitReactiveIntent(IpPrefix ipPrefix, MultiPointToSinglePointIntent intent) {
284 synchronized (this) {
285 // Store the intent in memory
286 routeIntents.put(ipPrefix, intent);
287
288 // Push the intent
289 if (isElectedLeader && isActivatedLeader) {
290 log.trace("SDN-IP submitting reactive routing intent: {}", intent);
291 intentService.submit(intent);
292 }
293 }
294 }
295
296 /**
Jonathan Hart552e31f2015-02-06 11:11:59 -0800297 * Generates a route intent for a prefix, the next hop IP address, and
298 * the next hop MAC address.
299 * <p/>
300 * This method will find the egress interface for the intent.
301 * Intent will match dst IP prefix and rewrite dst MAC address at all other
302 * border switches, then forward packets according to dst MAC address.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800303 *
Jonathan Hart552e31f2015-02-06 11:11:59 -0800304 * @param prefix IP prefix of the route to add
305 * @param nextHopIpAddress IP address of the next hop
306 * @param nextHopMacAddress MAC address of the next hop
307 * @return the generated intent, or null if no intent should be submitted
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800308 */
Jonathan Hart552e31f2015-02-06 11:11:59 -0800309 private MultiPointToSinglePointIntent generateRouteIntent(
310 IpPrefix prefix,
311 IpAddress nextHopIpAddress,
312 MacAddress nextHopMacAddress) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800313
Jonathan Hart552e31f2015-02-06 11:11:59 -0800314 // Find the attachment point (egress interface) of the next hop
Jonathan Hart4cb39882015-08-12 23:50:55 -0400315 Interface egressInterface = interfaceService.getMatchingInterface(nextHopIpAddress);
316 if (egressInterface == null) {
317 log.warn("No outgoing interface found for {}",
318 nextHopIpAddress);
319 return null;
Jonathan Hart552e31f2015-02-06 11:11:59 -0800320 }
321
322 //
323 // Generate the intent itself
324 //
325 Set<ConnectPoint> ingressPorts = new HashSet<>();
326 ConnectPoint egressPort = egressInterface.connectPoint();
327 log.debug("Generating intent for prefix {}, next hop mac {}",
328 prefix, nextHopMacAddress);
329
Jonathan Hart4cb39882015-08-12 23:50:55 -0400330 for (Interface intf : interfaceService.getInterfaces()) {
331 // TODO this should be only peering interfaces
Jonathan Hart552e31f2015-02-06 11:11:59 -0800332 if (!intf.connectPoint().equals(egressInterface.connectPoint())) {
333 ConnectPoint srcPort = intf.connectPoint();
334 ingressPorts.add(srcPort);
335 }
336 }
337
338 // Match the destination IP prefix at the first hop
339 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700340 if (prefix.isIp4()) {
Jonathan Hart552e31f2015-02-06 11:11:59 -0800341 selector.matchEthType(Ethernet.TYPE_IPV4);
Pavlin Radoslavova8537092015-02-23 10:15:20 -0800342 selector.matchIPDst(prefix);
Jonathan Hart552e31f2015-02-06 11:11:59 -0800343 } else {
344 selector.matchEthType(Ethernet.TYPE_IPV6);
Pavlin Radoslavova8537092015-02-23 10:15:20 -0800345 selector.matchIPv6Dst(prefix);
Jonathan Hart552e31f2015-02-06 11:11:59 -0800346 }
Jonathan Hart552e31f2015-02-06 11:11:59 -0800347
348 // Rewrite the destination MAC address
349 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
350 .setEthDst(nextHopMacAddress);
351 if (!egressInterface.vlan().equals(VlanId.NONE)) {
352 treatment.setVlanId(egressInterface.vlan());
353 // If we set VLAN ID, we have to make sure a VLAN tag exists.
354 // TODO support no VLAN -> VLAN routing
355 selector.matchVlanId(VlanId.ANY);
356 }
357
Pavlin Radoslavov2aa1f322015-03-11 17:59:44 -0700358 int priority =
359 prefix.prefixLength() * PRIORITY_MULTIPLIER + PRIORITY_OFFSET;
360 Key key = Key.of(prefix.toString(), appId);
Ray Milkeyebc5d222015-03-18 15:45:36 -0700361 return MultiPointToSinglePointIntent.builder()
362 .appId(appId)
363 .key(key)
364 .selector(selector.build())
365 .treatment(treatment.build())
366 .ingressPoints(ingressPorts)
367 .egressPoint(egressPort)
368 .priority(priority)
Jonathan Hart96c5a4a2015-07-31 14:23:33 -0700369 .constraints(CONSTRAINTS)
Ray Milkeyebc5d222015-03-18 15:45:36 -0700370 .build();
Jonathan Hart552e31f2015-02-06 11:11:59 -0800371 }
372
373 @Override
Pingping Line28ae4c2015-03-13 11:37:03 -0700374 public void setUpConnectivityInternetToHost(IpAddress hostIpAddress) {
375 checkNotNull(hostIpAddress);
Pingping Lin8a524712015-06-24 14:58:24 -0700376 Set<ConnectPoint> ingressPoints =
377 configService.getBgpPeerConnectPoints();
378
Pingping Line28ae4c2015-03-13 11:37:03 -0700379 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
380
381 if (hostIpAddress.isIp4()) {
382 selector.matchEthType(Ethernet.TYPE_IPV4);
383 } else {
384 selector.matchEthType(Ethernet.TYPE_IPV6);
385 }
386
387 // Match the destination IP prefix at the first hop
388 IpPrefix ipPrefix = hostIpAddress.toIpPrefix();
389 selector.matchIPDst(ipPrefix);
390
391 // Rewrite the destination MAC address
392 MacAddress hostMac = null;
393 ConnectPoint egressPoint = null;
394 for (Host host : hostService.getHostsByIp(hostIpAddress)) {
395 if (host.mac() != null) {
396 hostMac = host.mac();
397 egressPoint = host.location();
398 break;
399 }
400 }
401 if (hostMac == null) {
402 hostService.startMonitoringIp(hostIpAddress);
403 return;
404 }
405
406 TrafficTreatment.Builder treatment =
407 DefaultTrafficTreatment.builder().setEthDst(hostMac);
408 Key key = Key.of(ipPrefix.toString(), appId);
409 int priority = ipPrefix.prefixLength() * PRIORITY_MULTIPLIER
410 + PRIORITY_OFFSET;
411 MultiPointToSinglePointIntent intent =
412 MultiPointToSinglePointIntent.builder()
413 .appId(appId)
414 .key(key)
415 .selector(selector.build())
416 .treatment(treatment.build())
417 .ingressPoints(ingressPoints)
418 .egressPoint(egressPoint)
419 .priority(priority)
Jonathan Hart96c5a4a2015-07-31 14:23:33 -0700420 .constraints(CONSTRAINTS)
Pingping Line28ae4c2015-03-13 11:37:03 -0700421 .build();
422
423 log.trace("Generates ConnectivityInternetToHost intent {}", intent);
424 submitReactiveIntent(ipPrefix, intent);
425 }
426
427
428 @Override
Jonathan Hart552e31f2015-02-06 11:11:59 -0800429 public void update(Collection<FibUpdate> updates, Collection<FibUpdate> withdraws) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800430 //
431 // NOTE: Semantically, we MUST withdraw existing intents before
432 // submitting new intents.
433 //
434 synchronized (this) {
435 MultiPointToSinglePointIntent intent;
436
437 log.debug("SDN-IP submitting intents = {} withdrawing = {}",
Jonathan Hart552e31f2015-02-06 11:11:59 -0800438 updates.size(), withdraws.size());
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800439
440 //
441 // Prepare the Intent batch operations for the intents to withdraw
442 //
Jonathan Hart552e31f2015-02-06 11:11:59 -0800443 for (FibUpdate withdraw : withdraws) {
444 checkArgument(withdraw.type() == FibUpdate.Type.DELETE,
445 "FibUpdate with wrong type in withdraws list");
446
447 IpPrefix prefix = withdraw.entry().prefix();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800448 intent = routeIntents.remove(prefix);
449 if (intent == null) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800450 log.trace("SDN-IP No intent in routeIntents to delete " +
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800451 "for prefix: {}", prefix);
452 continue;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800453 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800454 if (isElectedLeader && isActivatedLeader) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800455 log.trace("SDN-IP Withdrawing intent: {}", intent);
Brian O'Connor03406a42015-02-03 17:28:57 -0800456 intentService.withdraw(intent);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800457 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800458 }
459
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800460 //
461 // Prepare the Intent batch operations for the intents to submit
462 //
Jonathan Hart552e31f2015-02-06 11:11:59 -0800463 for (FibUpdate update : updates) {
464 checkArgument(update.type() == FibUpdate.Type.UPDATE,
465 "FibUpdate with wrong type in updates list");
466
467 IpPrefix prefix = update.entry().prefix();
468 intent = generateRouteIntent(prefix, update.entry().nextHopIp(),
469 update.entry().nextHopMac());
470
471 if (intent == null) {
472 // This preserves the old semantics - if an intent can't be
473 // generated, we don't do anything with that prefix. But
474 // perhaps we should withdraw the old intent anyway?
475 continue;
476 }
477
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800478 MultiPointToSinglePointIntent oldIntent =
479 routeIntents.put(prefix, intent);
480 if (isElectedLeader && isActivatedLeader) {
481 if (oldIntent != null) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800482 log.trace("SDN-IP Withdrawing old intent: {}",
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800483 oldIntent);
Brian O'Connor03406a42015-02-03 17:28:57 -0800484 intentService.withdraw(oldIntent);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800485 }
Jonathan Hartb3b8a0a2015-02-12 17:36:13 -0800486 log.trace("SDN-IP Submitting intent: {}", intent);
487 intentService.submit(intent);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800488 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800489 }
490 }
491 }
492
493 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800494 * Synchronize the in-memory Intents with the Intents in the Intent
495 * framework.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800496 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800497 void synchronizeIntents() {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800498 synchronized (this) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800499
500 Map<IntentKey, Intent> localIntents = new HashMap<>();
501 Map<IntentKey, Intent> fetchedIntents = new HashMap<>();
502 Collection<Intent> storeInMemoryIntents = new LinkedList<>();
503 Collection<Intent> addIntents = new LinkedList<>();
504 Collection<Intent> deleteIntents = new LinkedList<>();
505
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800506 if (!isElectedLeader) {
507 return; // Nothing to do: not the leader anymore
508 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800509 log.debug("SDN-IP synchronizing all intents...");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800510
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800511 // Prepare the local intents
512 for (Intent intent : routeIntents.values()) {
513 localIntents.put(new IntentKey(intent), intent);
514 }
515 for (Intent intent : peerIntents.values()) {
516 localIntents.put(new IntentKey(intent), intent);
517 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800518
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800519 // Fetch all intents for this application
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800520 for (Intent intent : intentService.getIntents()) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800521 if (!intent.appId().equals(appId)) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800522 continue;
523 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800524 fetchedIntents.put(new IntentKey(intent), intent);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800525 }
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800526 if (log.isDebugEnabled()) {
527 for (Intent intent: fetchedIntents.values()) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800528 log.trace("SDN-IP Intent Synchronizer: fetched intent: {}",
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800529 intent);
530 }
531 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800532
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800533 computeIntentsDelta(localIntents, fetchedIntents,
534 storeInMemoryIntents, addIntents,
535 deleteIntents);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800536
537 //
538 // Perform the actions:
539 // 1. Store in memory fetched intents that are same. Can be done
540 // even if we are not the leader anymore
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800541 // 2. Delete intents: check if the leader before the operation
542 // 3. Add intents: check if the leader before the operation
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800543 //
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800544 for (Intent intent : storeInMemoryIntents) {
545 // Store the intent in memory based on its type
546 if (intent instanceof MultiPointToSinglePointIntent) {
547 MultiPointToSinglePointIntent mp2pIntent =
548 (MultiPointToSinglePointIntent) intent;
549 // Find the IP prefix
550 Criterion c =
551 mp2pIntent.selector().getCriterion(Criterion.Type.IPV4_DST);
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800552 if (c == null) {
553 // Try IPv6
554 c =
555 mp2pIntent.selector().getCriterion(Criterion.Type.IPV6_DST);
556 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800557 if (c != null && c instanceof IPCriterion) {
558 IPCriterion ipCriterion = (IPCriterion) c;
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800559 IpPrefix ipPrefix = ipCriterion.ip();
560 if (ipPrefix == null) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800561 continue;
562 }
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800563 log.trace("SDN-IP Intent Synchronizer: updating " +
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800564 "in-memory Route Intent for prefix {}",
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800565 ipPrefix);
566 routeIntents.put(ipPrefix, mp2pIntent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800567 } else {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800568 log.warn("SDN-IP no IPV4_DST or IPV6_DST criterion found for Intent {}",
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800569 mp2pIntent.id());
570 }
571 continue;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800572 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800573 if (intent instanceof PointToPointIntent) {
574 PointToPointIntent p2pIntent = (PointToPointIntent) intent;
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800575 log.trace("SDN-IP Intent Synchronizer: updating " +
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800576 "in-memory Peer Intent {}", p2pIntent);
577 peerIntents.put(new IntentKey(intent), p2pIntent);
578 continue;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800579 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800580 }
581
582 // Withdraw Intents
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800583 for (Intent intent : deleteIntents) {
Brian O'Connor03406a42015-02-03 17:28:57 -0800584 intentService.withdraw(intent);
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800585 log.trace("SDN-IP Intent Synchronizer: withdrawing intent: {}",
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800586 intent);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800587 }
588 if (!isElectedLeader) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800589 log.trace("SDN-IP Intent Synchronizer: cannot withdraw intents: " +
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800590 "not elected leader anymore");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800591 isActivatedLeader = false;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800592 return;
593 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800594
595 // Add Intents
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800596 for (Intent intent : addIntents) {
Brian O'Connor03406a42015-02-03 17:28:57 -0800597 intentService.submit(intent);
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800598 log.trace("SDN-IP Intent Synchronizer: submitting intent: {}",
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800599 intent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800600 }
601 if (!isElectedLeader) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800602 log.trace("SDN-IP Intent Synchronizer: cannot submit intents: " +
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800603 "not elected leader anymore");
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800604 isActivatedLeader = false;
605 return;
606 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800607
608 if (isElectedLeader) {
609 isActivatedLeader = true; // Allow push of Intents
610 } else {
611 isActivatedLeader = false;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800612 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800613 log.debug("SDN-IP intent synchronization completed");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800614 }
615 }
616
617 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800618 * Computes the delta in two sets of Intents: local in-memory Intents,
619 * and intents fetched from the Intent framework.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800620 *
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800621 * @param localIntents the local in-memory Intents
622 * @param fetchedIntents the Intents fetched from the Intent framework
623 * @param storeInMemoryIntents the Intents that should be stored in memory.
624 * Note: This Collection must be allocated by the caller, and it will
625 * be populated by this method.
626 * @param addIntents the Intents that should be added to the Intent
627 * framework. Note: This Collection must be allocated by the caller, and
628 * it will be populated by this method.
629 * @param deleteIntents the Intents that should be deleted from the Intent
630 * framework. Note: This Collection must be allocated by the caller, and
631 * it will be populated by this method.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800632 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800633 private void computeIntentsDelta(
634 final Map<IntentKey, Intent> localIntents,
635 final Map<IntentKey, Intent> fetchedIntents,
636 Collection<Intent> storeInMemoryIntents,
637 Collection<Intent> addIntents,
638 Collection<Intent> deleteIntents) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800639
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800640 //
641 // Compute the deltas between the LOCAL in-memory Intents and the
642 // FETCHED Intents:
643 // - If an Intent is in both the LOCAL and FETCHED sets:
644 // If the FETCHED Intent is WITHDRAWING or WITHDRAWN, then
645 // the LOCAL Intent should be added/installed; otherwise the
646 // FETCHED intent should be stored in the local memory
647 // (i.e., override the LOCAL Intent) to preserve the original
648 // Intent ID.
649 // - if a LOCAL Intent is not in the FETCHED set, then the LOCAL
650 // Intent should be added/installed.
651 // - If a FETCHED Intent is not in the LOCAL set, then the FETCHED
652 // Intent should be deleted/withdrawn.
653 //
654 for (Map.Entry<IntentKey, Intent> entry : localIntents.entrySet()) {
655 IntentKey intentKey = entry.getKey();
656 Intent localIntent = entry.getValue();
657 Intent fetchedIntent = fetchedIntents.get(intentKey);
658
659 if (fetchedIntent == null) {
660 //
661 // No FETCHED Intent found: push the LOCAL Intent.
662 //
663 addIntents.add(localIntent);
664 continue;
665 }
666
667 IntentState state =
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800668 intentService.getIntentState(fetchedIntent.key());
Pavlin Radoslavovdeb8a102014-11-26 13:31:36 -0800669 if (state == null ||
670 state == IntentState.WITHDRAWING ||
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800671 state == IntentState.WITHDRAWN) {
672 // The intent has been withdrawn but according to our route
673 // table it should be installed. We'll reinstall it.
674 addIntents.add(localIntent);
675 continue;
676 }
677 storeInMemoryIntents.add(fetchedIntent);
678 }
679
680 for (Map.Entry<IntentKey, Intent> entry : fetchedIntents.entrySet()) {
681 IntentKey intentKey = entry.getKey();
682 Intent fetchedIntent = entry.getValue();
683 Intent localIntent = localIntents.get(intentKey);
684
685 if (localIntent != null) {
686 continue;
687 }
688
689 IntentState state =
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800690 intentService.getIntentState(fetchedIntent.key());
Pavlin Radoslavovdeb8a102014-11-26 13:31:36 -0800691 if (state == null ||
692 state == IntentState.WITHDRAWING ||
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800693 state == IntentState.WITHDRAWN) {
694 // Nothing to do. The intent has been already withdrawn.
695 continue;
696 }
697 //
698 // No LOCAL Intent found: delete/withdraw the FETCHED Intent.
699 //
700 deleteIntents.add(fetchedIntent);
701 }
702 }
703
704 /**
705 * Helper class that can be used to compute the key for an Intent by
706 * by excluding the Intent ID.
707 */
708 static final class IntentKey {
709 private final Intent intent;
710
711 /**
712 * Constructor.
713 *
714 * @param intent the intent to use
715 */
716 IntentKey(Intent intent) {
717 checkArgument((intent instanceof MultiPointToSinglePointIntent) ||
718 (intent instanceof PointToPointIntent),
719 "Intent type not recognized", intent);
720 this.intent = intent;
721 }
722
723 /**
724 * Compares two Multi-Point to Single-Point Intents whether they
725 * represent same logical intention.
726 *
727 * @param intent1 the first Intent to compare
728 * @param intent2 the second Intent to compare
729 * @return true if both Intents represent same logical intention,
730 * otherwise false
731 */
732 static boolean equalIntents(MultiPointToSinglePointIntent intent1,
733 MultiPointToSinglePointIntent intent2) {
734 return Objects.equals(intent1.appId(), intent2.appId()) &&
735 Objects.equals(intent1.selector(), intent2.selector()) &&
736 Objects.equals(intent1.treatment(), intent2.treatment()) &&
737 Objects.equals(intent1.ingressPoints(), intent2.ingressPoints()) &&
738 Objects.equals(intent1.egressPoint(), intent2.egressPoint());
739 }
740
741 /**
742 * Compares two Point-to-Point Intents whether they represent
743 * same logical intention.
744 *
745 * @param intent1 the first Intent to compare
746 * @param intent2 the second Intent to compare
747 * @return true if both Intents represent same logical intention,
748 * otherwise false
749 */
750 static boolean equalIntents(PointToPointIntent intent1,
751 PointToPointIntent intent2) {
752 return Objects.equals(intent1.appId(), intent2.appId()) &&
753 Objects.equals(intent1.selector(), intent2.selector()) &&
754 Objects.equals(intent1.treatment(), intent2.treatment()) &&
755 Objects.equals(intent1.ingressPoint(), intent2.ingressPoint()) &&
756 Objects.equals(intent1.egressPoint(), intent2.egressPoint());
757 }
758
759 @Override
760 public int hashCode() {
761 if (intent instanceof PointToPointIntent) {
762 PointToPointIntent p2pIntent = (PointToPointIntent) intent;
763 return Objects.hash(p2pIntent.appId(),
764 p2pIntent.resources(),
765 p2pIntent.selector(),
766 p2pIntent.treatment(),
767 p2pIntent.constraints(),
768 p2pIntent.ingressPoint(),
769 p2pIntent.egressPoint());
770 }
771 if (intent instanceof MultiPointToSinglePointIntent) {
772 MultiPointToSinglePointIntent m2pIntent =
773 (MultiPointToSinglePointIntent) intent;
774 return Objects.hash(m2pIntent.appId(),
775 m2pIntent.resources(),
776 m2pIntent.selector(),
777 m2pIntent.treatment(),
778 m2pIntent.constraints(),
779 m2pIntent.ingressPoints(),
780 m2pIntent.egressPoint());
781 }
782 checkArgument(false, "Intent type not recognized", intent);
783 return 0;
784 }
785
786 @Override
787 public boolean equals(Object obj) {
788 if (this == obj) {
789 return true;
790 }
791 if ((obj == null) || (!(obj instanceof IntentKey))) {
792 return false;
793 }
794 IntentKey other = (IntentKey) obj;
795
796 if (this.intent instanceof PointToPointIntent) {
797 if (!(other.intent instanceof PointToPointIntent)) {
798 return false;
799 }
800 return equalIntents((PointToPointIntent) this.intent,
801 (PointToPointIntent) other.intent);
802 }
803 if (this.intent instanceof MultiPointToSinglePointIntent) {
804 if (!(other.intent instanceof MultiPointToSinglePointIntent)) {
805 return false;
806 }
807 return equalIntents(
808 (MultiPointToSinglePointIntent) this.intent,
809 (MultiPointToSinglePointIntent) other.intent);
810 }
811 checkArgument(false, "Intent type not recognized", intent);
812 return false;
813 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800814 }
Pingping Line28ae4c2015-03-13 11:37:03 -0700815
816 @Override
817 public void setUpConnectivityHostToHost(IpAddress dstIpAddress,
818 IpAddress srcIpAddress,
819 MacAddress srcMacAddress,
820 ConnectPoint srcConnectPoint) {
821 checkNotNull(dstIpAddress);
822 checkNotNull(srcIpAddress);
823 checkNotNull(srcMacAddress);
824 checkNotNull(srcConnectPoint);
825
826 IpPrefix srcIpPrefix = srcIpAddress.toIpPrefix();
827 IpPrefix dstIpPrefix = dstIpAddress.toIpPrefix();
828 ConnectPoint dstConnectPoint = null;
829 MacAddress dstMacAddress = null;
830
831 for (Host host : hostService.getHostsByIp(dstIpAddress)) {
832 if (host.mac() != null) {
833 dstMacAddress = host.mac();
834 dstConnectPoint = host.location();
835 break;
836 }
837 }
838 if (dstMacAddress == null) {
839 hostService.startMonitoringIp(dstIpAddress);
840 return;
841 }
842
843 //
844 // Handle intent from source host to destination host
845 //
846 MultiPointToSinglePointIntent srcToDstIntent =
847 hostToHostIntentGenerator(dstIpAddress, dstConnectPoint,
848 dstMacAddress, srcConnectPoint);
849 submitReactiveIntent(dstIpPrefix, srcToDstIntent);
850
851 //
852 // Handle intent from destination host to source host
853 //
854
855 // Since we proactively handle the intent from destination host to
856 // source host, we should check whether there is an exiting intent
857 // first.
858 if (mp2pIntentExists(srcIpPrefix)) {
859 updateExistingMp2pIntent(srcIpPrefix, dstConnectPoint);
860 return;
861 } else {
862 // There is no existing intent, create a new one.
863 MultiPointToSinglePointIntent dstToSrcIntent =
864 hostToHostIntentGenerator(srcIpAddress, srcConnectPoint,
865 srcMacAddress, dstConnectPoint);
866 submitReactiveIntent(srcIpPrefix, dstToSrcIntent);
867 }
868 }
869
870 /**
871 * Generates MultiPointToSinglePointIntent for both source host and
872 * destination host located in local SDN network.
873 *
874 * @param dstIpAddress the destination IP address
875 * @param dstConnectPoint the destination host connect point
876 * @param dstMacAddress the MAC address of destination host
877 * @param srcConnectPoint the connect point where packet-in from
878 * @return the generated MultiPointToSinglePointIntent
879 */
880 private MultiPointToSinglePointIntent hostToHostIntentGenerator(
881 IpAddress dstIpAddress,
882 ConnectPoint dstConnectPoint,
883 MacAddress dstMacAddress,
884 ConnectPoint srcConnectPoint) {
885 checkNotNull(dstIpAddress);
886 checkNotNull(dstConnectPoint);
887 checkNotNull(dstMacAddress);
888 checkNotNull(srcConnectPoint);
889
890 Set<ConnectPoint> ingressPoints = new HashSet<ConnectPoint>();
891 ingressPoints.add(srcConnectPoint);
892 IpPrefix dstIpPrefix = dstIpAddress.toIpPrefix();
893
894 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
895 if (dstIpAddress.isIp4()) {
896 selector.matchEthType(Ethernet.TYPE_IPV4);
897 selector.matchIPDst(dstIpPrefix);
898 } else {
899 selector.matchEthType(Ethernet.TYPE_IPV6);
900 selector.matchIPv6Dst(dstIpPrefix);
901 }
902
903 // Rewrite the destination MAC address
904 TrafficTreatment.Builder treatment =
905 DefaultTrafficTreatment.builder().setEthDst(dstMacAddress);
906
907 Key key = Key.of(dstIpPrefix.toString(), appId);
908 int priority = dstIpPrefix.prefixLength() * PRIORITY_MULTIPLIER
909 + PRIORITY_OFFSET;
910 MultiPointToSinglePointIntent intent =
911 MultiPointToSinglePointIntent.builder()
912 .appId(appId)
913 .key(key)
914 .selector(selector.build())
915 .treatment(treatment.build())
916 .ingressPoints(ingressPoints)
917 .egressPoint(dstConnectPoint)
918 .priority(priority)
Jonathan Hart96c5a4a2015-07-31 14:23:33 -0700919 .constraints(CONSTRAINTS)
Pingping Line28ae4c2015-03-13 11:37:03 -0700920 .build();
921
922 log.trace("Generates ConnectivityHostToHost = {} ", intent);
923 return intent;
924 }
925
926 @Override
927 public void updateExistingMp2pIntent(IpPrefix ipPrefix,
928 ConnectPoint ingressConnectPoint) {
929 checkNotNull(ipPrefix);
930 checkNotNull(ingressConnectPoint);
931
932 MultiPointToSinglePointIntent existingIntent =
933 getExistingMp2pIntent(ipPrefix);
934 if (existingIntent != null) {
935 Set<ConnectPoint> ingressPoints = existingIntent.ingressPoints();
936 // Add host connect point into ingressPoints of the existing intent
937 if (ingressPoints.add(ingressConnectPoint)) {
938 MultiPointToSinglePointIntent updatedMp2pIntent =
939 MultiPointToSinglePointIntent.builder()
940 .appId(appId)
941 .key(existingIntent.key())
942 .selector(existingIntent.selector())
943 .treatment(existingIntent.treatment())
944 .ingressPoints(ingressPoints)
945 .egressPoint(existingIntent.egressPoint())
946 .priority(existingIntent.priority())
Jonathan Hart96c5a4a2015-07-31 14:23:33 -0700947 .constraints(CONSTRAINTS)
Pingping Line28ae4c2015-03-13 11:37:03 -0700948 .build();
949
950 log.trace("Update an existing MultiPointToSinglePointIntent "
951 + "to new intent = {} ", updatedMp2pIntent);
952 submitReactiveIntent(ipPrefix, updatedMp2pIntent);
953 }
954 // If adding ingressConnectPoint to ingressPoints failed, it
955 // because between the time interval from checking existing intent
956 // to generating new intent, onos updated this intent due to other
957 // packet-in and the new intent also includes the
958 // ingressConnectPoint. This will not affect reactive routing.
959 }
960 }
961
962 @Override
963 public boolean mp2pIntentExists(IpPrefix ipPrefix) {
964 checkNotNull(ipPrefix);
Sho SHIMIZUfe89d3a2015-06-30 18:15:39 -0700965 return routeIntents.get(ipPrefix) != null;
Pingping Line28ae4c2015-03-13 11:37:03 -0700966 }
967
968 /**
969 * Gets the existing MultiPointToSinglePointIntent from memory for a given
970 * IP prefix.
971 *
972 * @param ipPrefix the IP prefix used to find MultiPointToSinglePointIntent
973 * @return the MultiPointToSinglePointIntent if found, otherwise null
974 */
975 private MultiPointToSinglePointIntent getExistingMp2pIntent(IpPrefix
976 ipPrefix) {
977 checkNotNull(ipPrefix);
978 return routeIntents.get(ipPrefix);
979 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800980}