blob: 8e089cd9a791532863177b9d095109ab5e699334 [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
Ray Milkeyebc5d222015-03-18 15:45:36 -070018import java.util.Collection;
19import java.util.HashMap;
20import java.util.HashSet;
21import java.util.LinkedList;
22import java.util.List;
23import java.util.Map;
24import java.util.Objects;
25import java.util.Set;
26import java.util.concurrent.ConcurrentHashMap;
27import java.util.concurrent.ExecutorService;
28import java.util.concurrent.Executors;
29import java.util.concurrent.Semaphore;
30
Jonathan Hart552e31f2015-02-06 11:11:59 -080031import org.onlab.packet.Ethernet;
Jonathan Hart552e31f2015-02-06 11:11:59 -080032import org.onlab.packet.IpAddress;
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080033import org.onlab.packet.IpPrefix;
Jonathan Hart552e31f2015-02-06 11:11:59 -080034import org.onlab.packet.MacAddress;
35import org.onlab.packet.VlanId;
Brian O'Connorabafb502014-12-02 22:26:20 -080036import org.onosproject.core.ApplicationId;
Jonathan Hart552e31f2015-02-06 11:11:59 -080037import org.onosproject.net.ConnectPoint;
Pingping Line28ae4c2015-03-13 11:37:03 -070038import org.onosproject.net.Host;
Jonathan Hart552e31f2015-02-06 11:11:59 -080039import org.onosproject.net.flow.DefaultTrafficSelector;
40import org.onosproject.net.flow.DefaultTrafficTreatment;
41import org.onosproject.net.flow.TrafficSelector;
42import org.onosproject.net.flow.TrafficTreatment;
Brian O'Connorabafb502014-12-02 22:26:20 -080043import org.onosproject.net.flow.criteria.Criteria.IPCriterion;
44import org.onosproject.net.flow.criteria.Criterion;
Pingping Line28ae4c2015-03-13 11:37:03 -070045import org.onosproject.net.host.HostService;
Brian O'Connorabafb502014-12-02 22:26:20 -080046import org.onosproject.net.intent.Intent;
Brian O'Connorabafb502014-12-02 22:26:20 -080047import org.onosproject.net.intent.IntentService;
48import org.onosproject.net.intent.IntentState;
Pavlin Radoslavov2aa1f322015-03-11 17:59:44 -070049import org.onosproject.net.intent.Key;
Brian O'Connorabafb502014-12-02 22:26:20 -080050import org.onosproject.net.intent.MultiPointToSinglePointIntent;
51import org.onosproject.net.intent.PointToPointIntent;
Jonathan Hart2da1e602015-02-18 19:09:24 -080052import org.onosproject.routing.FibListener;
53import org.onosproject.routing.FibUpdate;
Pingping Line28ae4c2015-03-13 11:37:03 -070054import org.onosproject.routing.IntentRequestListener;
Jonathan Hart2da1e602015-02-18 19:09:24 -080055import org.onosproject.routing.config.BgpPeer;
56import org.onosproject.routing.config.Interface;
57import org.onosproject.routing.config.RoutingConfigurationService;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080058import org.slf4j.Logger;
59import org.slf4j.LoggerFactory;
60
Ray Milkeyebc5d222015-03-18 15:45:36 -070061import com.google.common.util.concurrent.ThreadFactoryBuilder;
Jonathan Hart552e31f2015-02-06 11:11:59 -080062
63import static com.google.common.base.Preconditions.checkArgument;
Pingping Line28ae4c2015-03-13 11:37:03 -070064import static com.google.common.base.Preconditions.checkNotNull;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080065
Jonathan Hart51372182014-12-03 21:32:34 -080066/**
67 * Synchronizes intents between the in-memory intent store and the
68 * IntentService.
69 */
Pingping Line28ae4c2015-03-13 11:37:03 -070070public class IntentSynchronizer implements FibListener, IntentRequestListener {
Pavlin Radoslavov2aa1f322015-03-11 17:59:44 -070071 private static final int PRIORITY_OFFSET = 100;
72 private static final int PRIORITY_MULTIPLIER = 5;
73
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080074 private static final Logger log =
75 LoggerFactory.getLogger(IntentSynchronizer.class);
76
77 private final ApplicationId appId;
78 private final IntentService intentService;
Pingping Line28ae4c2015-03-13 11:37:03 -070079 private final HostService hostService;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -080080 private final Map<IntentKey, PointToPointIntent> peerIntents;
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080081 private final Map<IpPrefix, MultiPointToSinglePointIntent> routeIntents;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080082
83 //
84 // State to deal with SDN-IP Leader election and pushing Intents
85 //
86 private final ExecutorService bgpIntentsSynchronizerExecutor;
87 private final Semaphore intentsSynchronizerSemaphore = new Semaphore(0);
88 private volatile boolean isElectedLeader = false;
89 private volatile boolean isActivatedLeader = false;
90
Jonathan Hart90a02c22015-02-13 11:52:07 -080091 private final RoutingConfigurationService configService;
Jonathan Hart552e31f2015-02-06 11:11:59 -080092
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080093 /**
94 * Class constructor.
95 *
96 * @param appId the Application ID
97 * @param intentService the intent service
Pingping Line28ae4c2015-03-13 11:37:03 -070098 * @param hostService the host service
Jonathan Hart552e31f2015-02-06 11:11:59 -080099 * @param configService the SDN-IP configuration service
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800100 */
Jonathan Hart552e31f2015-02-06 11:11:59 -0800101 IntentSynchronizer(ApplicationId appId, IntentService intentService,
Pingping Line28ae4c2015-03-13 11:37:03 -0700102 HostService hostService,
Jonathan Hart90a02c22015-02-13 11:52:07 -0800103 RoutingConfigurationService configService) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800104 this.appId = appId;
105 this.intentService = intentService;
Pingping Line28ae4c2015-03-13 11:37:03 -0700106 this.hostService = hostService;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800107 peerIntents = new ConcurrentHashMap<>();
108 routeIntents = new ConcurrentHashMap<>();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800109
Jonathan Hart552e31f2015-02-06 11:11:59 -0800110 this.configService = configService;
Jonathan Hart552e31f2015-02-06 11:11:59 -0800111
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800112 bgpIntentsSynchronizerExecutor = Executors.newSingleThreadExecutor(
113 new ThreadFactoryBuilder()
Pavlin Radoslavov8b752442014-11-18 14:34:37 -0800114 .setNameFormat("sdnip-intents-synchronizer-%d").build());
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800115 }
116
117 /**
118 * Starts the synchronizer.
119 */
120 public void start() {
121 bgpIntentsSynchronizerExecutor.execute(new Runnable() {
122 @Override
123 public void run() {
124 doIntentSynchronizationThread();
125 }
126 });
127 }
128
129 /**
130 * Stops the synchronizer.
131 */
132 public void stop() {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800133 synchronized (this) {
134 // Stop the thread(s)
135 bgpIntentsSynchronizerExecutor.shutdownNow();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800136
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800137 //
138 // Withdraw all SDN-IP intents
139 //
140 if (!isElectedLeader) {
141 return; // Nothing to do: not the leader anymore
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800142 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800143
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800144 //
Pavlin Radoslavov20be3e62014-11-25 18:52:08 -0800145 // NOTE: We don't withdraw the intents during shutdown, because
146 // it creates flux in the data plane during switchover.
147 //
148
149 /*
150 //
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800151 // Build a batch operation to withdraw all intents from this
152 // application.
153 //
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800154 log.debug("SDN-IP Intent Synchronizer shutdown: " +
155 "withdrawing all intents...");
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800156 IntentOperations.Builder builder = IntentOperations.builder(appId);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800157 for (Intent intent : intentService.getIntents()) {
158 // Skip the intents from other applications
159 if (!intent.appId().equals(appId)) {
160 continue;
161 }
162
163 // Skip the intents that are already withdrawn
164 IntentState intentState =
165 intentService.getIntentState(intent.id());
Pavlin Radoslavovdeb8a102014-11-26 13:31:36 -0800166 if ((intentState == null) ||
167 intentState.equals(IntentState.WITHDRAWING) ||
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800168 intentState.equals(IntentState.WITHDRAWN)) {
169 continue;
170 }
171
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800172 log.trace("SDN-IP Intent Synchronizer withdrawing intent: {}",
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800173 intent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800174 builder.addWithdrawOperation(intent.id());
175 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800176 IntentOperations intentOperations = builder.build();
177 intentService.execute(intentOperations);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800178 leaderChanged(false);
179
180 peerIntents.clear();
181 routeIntents.clear();
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800182 log.debug("SDN-IP Intent Synchronizer shutdown completed");
Pavlin Radoslavov20be3e62014-11-25 18:52:08 -0800183 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800184 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800185 }
186
Jonathan Hart51372182014-12-03 21:32:34 -0800187 /**
188 * Signals the synchronizer that the SDN-IP leadership has changed.
189 *
190 * @param isLeader true if this instance is now the leader, otherwise false
191 */
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800192 public void leaderChanged(boolean isLeader) {
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800193 log.debug("SDN-IP Leader changed: {}", isLeader);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800194
195 if (!isLeader) {
196 this.isElectedLeader = false;
197 this.isActivatedLeader = false;
198 return; // Nothing to do
199 }
200 this.isActivatedLeader = false;
201 this.isElectedLeader = true;
202
203 //
204 // Tell the Intents Synchronizer thread to start the synchronization
205 //
206 intentsSynchronizerSemaphore.release();
207 }
208
209 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800210 * Gets the route intents.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800211 *
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800212 * @return the route intents
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800213 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800214 public Collection<MultiPointToSinglePointIntent> getRouteIntents() {
215 List<MultiPointToSinglePointIntent> result = new LinkedList<>();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800216
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800217 for (Map.Entry<IpPrefix, MultiPointToSinglePointIntent> entry :
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800218 routeIntents.entrySet()) {
219 result.add(entry.getValue());
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800220 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800221 return result;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800222 }
223
224 /**
225 * Thread for Intent Synchronization.
226 */
227 private void doIntentSynchronizationThread() {
228 boolean interrupted = false;
229 try {
230 while (!interrupted) {
231 try {
232 intentsSynchronizerSemaphore.acquire();
233 //
234 // Drain all permits, because a single synchronization is
235 // sufficient.
236 //
237 intentsSynchronizerSemaphore.drainPermits();
238 } catch (InterruptedException e) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800239 interrupted = true;
240 break;
241 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800242 synchronizeIntents();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800243 }
244 } finally {
245 if (interrupted) {
246 Thread.currentThread().interrupt();
247 }
248 }
249 }
250
251 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800252 * Submits a collection of point-to-point intents.
253 *
254 * @param intents the intents to submit
255 */
256 void submitPeerIntents(Collection<PointToPointIntent> intents) {
257 synchronized (this) {
258 // Store the intents in memory
259 for (PointToPointIntent intent : intents) {
260 peerIntents.put(new IntentKey(intent), intent);
261 }
262
263 // Push the intents
264 if (isElectedLeader && isActivatedLeader) {
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800265 log.debug("SDN-IP Submitting all Peer Intents...");
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800266 for (Intent intent : intents) {
Brian O'Connor03406a42015-02-03 17:28:57 -0800267 log.trace("SDN-IP Submitting intents: {}", intent);
268 intentService.submit(intent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800269 }
270 }
271 }
272 }
273
274 /**
Pingping Line28ae4c2015-03-13 11:37:03 -0700275 * Submits a MultiPointToSinglePointIntent for reactive routing.
276 *
277 * @param ipPrefix the IP prefix to match in a MultiPointToSinglePointIntent
278 * @param intent the intent to submit
279 */
280 void submitReactiveIntent(IpPrefix ipPrefix, MultiPointToSinglePointIntent intent) {
281 synchronized (this) {
282 // Store the intent in memory
283 routeIntents.put(ipPrefix, intent);
284
285 // Push the intent
286 if (isElectedLeader && isActivatedLeader) {
287 log.trace("SDN-IP submitting reactive routing intent: {}", intent);
288 intentService.submit(intent);
289 }
290 }
291 }
292
293 /**
Jonathan Hart552e31f2015-02-06 11:11:59 -0800294 * Generates a route intent for a prefix, the next hop IP address, and
295 * the next hop MAC address.
296 * <p/>
297 * This method will find the egress interface for the intent.
298 * Intent will match dst IP prefix and rewrite dst MAC address at all other
299 * border switches, then forward packets according to dst MAC address.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800300 *
Jonathan Hart552e31f2015-02-06 11:11:59 -0800301 * @param prefix IP prefix of the route to add
302 * @param nextHopIpAddress IP address of the next hop
303 * @param nextHopMacAddress MAC address of the next hop
304 * @return the generated intent, or null if no intent should be submitted
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800305 */
Jonathan Hart552e31f2015-02-06 11:11:59 -0800306 private MultiPointToSinglePointIntent generateRouteIntent(
307 IpPrefix prefix,
308 IpAddress nextHopIpAddress,
309 MacAddress nextHopMacAddress) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800310
Jonathan Hart552e31f2015-02-06 11:11:59 -0800311 // Find the attachment point (egress interface) of the next hop
312 Interface egressInterface;
313 if (configService.getBgpPeers().containsKey(nextHopIpAddress)) {
314 // Route to a peer
315 log.debug("Route to peer {}", nextHopIpAddress);
316 BgpPeer peer =
317 configService.getBgpPeers().get(nextHopIpAddress);
318 egressInterface =
Jonathan Hart90a02c22015-02-13 11:52:07 -0800319 configService.getInterface(peer.connectPoint());
Jonathan Hart552e31f2015-02-06 11:11:59 -0800320 } else {
321 // Route to non-peer
322 log.debug("Route to non-peer {}", nextHopIpAddress);
323 egressInterface =
Jonathan Hart90a02c22015-02-13 11:52:07 -0800324 configService.getMatchingInterface(nextHopIpAddress);
Jonathan Hart552e31f2015-02-06 11:11:59 -0800325 if (egressInterface == null) {
326 log.warn("No outgoing interface found for {}",
327 nextHopIpAddress);
328 return null;
329 }
330 }
331
332 //
333 // Generate the intent itself
334 //
335 Set<ConnectPoint> ingressPorts = new HashSet<>();
336 ConnectPoint egressPort = egressInterface.connectPoint();
337 log.debug("Generating intent for prefix {}, next hop mac {}",
338 prefix, nextHopMacAddress);
339
Jonathan Hart90a02c22015-02-13 11:52:07 -0800340 for (Interface intf : configService.getInterfaces()) {
Jonathan Hart552e31f2015-02-06 11:11:59 -0800341 if (!intf.connectPoint().equals(egressInterface.connectPoint())) {
342 ConnectPoint srcPort = intf.connectPoint();
343 ingressPorts.add(srcPort);
344 }
345 }
346
347 // Match the destination IP prefix at the first hop
348 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700349 if (prefix.isIp4()) {
Jonathan Hart552e31f2015-02-06 11:11:59 -0800350 selector.matchEthType(Ethernet.TYPE_IPV4);
Pavlin Radoslavova8537092015-02-23 10:15:20 -0800351 selector.matchIPDst(prefix);
Jonathan Hart552e31f2015-02-06 11:11:59 -0800352 } else {
353 selector.matchEthType(Ethernet.TYPE_IPV6);
Pavlin Radoslavova8537092015-02-23 10:15:20 -0800354 selector.matchIPv6Dst(prefix);
Jonathan Hart552e31f2015-02-06 11:11:59 -0800355 }
Jonathan Hart552e31f2015-02-06 11:11:59 -0800356
357 // Rewrite the destination MAC address
358 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
359 .setEthDst(nextHopMacAddress);
360 if (!egressInterface.vlan().equals(VlanId.NONE)) {
361 treatment.setVlanId(egressInterface.vlan());
362 // If we set VLAN ID, we have to make sure a VLAN tag exists.
363 // TODO support no VLAN -> VLAN routing
364 selector.matchVlanId(VlanId.ANY);
365 }
366
Pavlin Radoslavov2aa1f322015-03-11 17:59:44 -0700367 int priority =
368 prefix.prefixLength() * PRIORITY_MULTIPLIER + PRIORITY_OFFSET;
369 Key key = Key.of(prefix.toString(), appId);
Ray Milkeyebc5d222015-03-18 15:45:36 -0700370 return MultiPointToSinglePointIntent.builder()
371 .appId(appId)
372 .key(key)
373 .selector(selector.build())
374 .treatment(treatment.build())
375 .ingressPoints(ingressPorts)
376 .egressPoint(egressPort)
377 .priority(priority)
378 .build();
Jonathan Hart552e31f2015-02-06 11:11:59 -0800379 }
380
381 @Override
Pingping Line28ae4c2015-03-13 11:37:03 -0700382 public void setUpConnectivityInternetToHost(IpAddress hostIpAddress) {
383 checkNotNull(hostIpAddress);
384 Set<ConnectPoint> ingressPoints = new HashSet<ConnectPoint>();
385 for (Interface intf : configService.getInterfaces()) {
386 ConnectPoint srcPoint = intf.connectPoint();
387 ingressPoints.add(srcPoint);
388 }
389 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
390
391 if (hostIpAddress.isIp4()) {
392 selector.matchEthType(Ethernet.TYPE_IPV4);
393 } else {
394 selector.matchEthType(Ethernet.TYPE_IPV6);
395 }
396
397 // Match the destination IP prefix at the first hop
398 IpPrefix ipPrefix = hostIpAddress.toIpPrefix();
399 selector.matchIPDst(ipPrefix);
400
401 // Rewrite the destination MAC address
402 MacAddress hostMac = null;
403 ConnectPoint egressPoint = null;
404 for (Host host : hostService.getHostsByIp(hostIpAddress)) {
405 if (host.mac() != null) {
406 hostMac = host.mac();
407 egressPoint = host.location();
408 break;
409 }
410 }
411 if (hostMac == null) {
412 hostService.startMonitoringIp(hostIpAddress);
413 return;
414 }
415
416 TrafficTreatment.Builder treatment =
417 DefaultTrafficTreatment.builder().setEthDst(hostMac);
418 Key key = Key.of(ipPrefix.toString(), appId);
419 int priority = ipPrefix.prefixLength() * PRIORITY_MULTIPLIER
420 + PRIORITY_OFFSET;
421 MultiPointToSinglePointIntent intent =
422 MultiPointToSinglePointIntent.builder()
423 .appId(appId)
424 .key(key)
425 .selector(selector.build())
426 .treatment(treatment.build())
427 .ingressPoints(ingressPoints)
428 .egressPoint(egressPoint)
429 .priority(priority)
430 .build();
431
432 log.trace("Generates ConnectivityInternetToHost intent {}", intent);
433 submitReactiveIntent(ipPrefix, intent);
434 }
435
436
437 @Override
Jonathan Hart552e31f2015-02-06 11:11:59 -0800438 public void update(Collection<FibUpdate> updates, Collection<FibUpdate> withdraws) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800439 //
440 // NOTE: Semantically, we MUST withdraw existing intents before
441 // submitting new intents.
442 //
443 synchronized (this) {
444 MultiPointToSinglePointIntent intent;
445
446 log.debug("SDN-IP submitting intents = {} withdrawing = {}",
Jonathan Hart552e31f2015-02-06 11:11:59 -0800447 updates.size(), withdraws.size());
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800448
449 //
450 // Prepare the Intent batch operations for the intents to withdraw
451 //
Jonathan Hart552e31f2015-02-06 11:11:59 -0800452 for (FibUpdate withdraw : withdraws) {
453 checkArgument(withdraw.type() == FibUpdate.Type.DELETE,
454 "FibUpdate with wrong type in withdraws list");
455
456 IpPrefix prefix = withdraw.entry().prefix();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800457 intent = routeIntents.remove(prefix);
458 if (intent == null) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800459 log.trace("SDN-IP No intent in routeIntents to delete " +
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800460 "for prefix: {}", prefix);
461 continue;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800462 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800463 if (isElectedLeader && isActivatedLeader) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800464 log.trace("SDN-IP Withdrawing intent: {}", intent);
Brian O'Connor03406a42015-02-03 17:28:57 -0800465 intentService.withdraw(intent);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800466 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800467 }
468
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800469 //
470 // Prepare the Intent batch operations for the intents to submit
471 //
Jonathan Hart552e31f2015-02-06 11:11:59 -0800472 for (FibUpdate update : updates) {
473 checkArgument(update.type() == FibUpdate.Type.UPDATE,
474 "FibUpdate with wrong type in updates list");
475
476 IpPrefix prefix = update.entry().prefix();
477 intent = generateRouteIntent(prefix, update.entry().nextHopIp(),
478 update.entry().nextHopMac());
479
480 if (intent == null) {
481 // This preserves the old semantics - if an intent can't be
482 // generated, we don't do anything with that prefix. But
483 // perhaps we should withdraw the old intent anyway?
484 continue;
485 }
486
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800487 MultiPointToSinglePointIntent oldIntent =
488 routeIntents.put(prefix, intent);
489 if (isElectedLeader && isActivatedLeader) {
490 if (oldIntent != null) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800491 log.trace("SDN-IP Withdrawing old intent: {}",
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800492 oldIntent);
Brian O'Connor03406a42015-02-03 17:28:57 -0800493 intentService.withdraw(oldIntent);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800494 }
Jonathan Hartb3b8a0a2015-02-12 17:36:13 -0800495 log.trace("SDN-IP Submitting intent: {}", intent);
496 intentService.submit(intent);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800497 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800498 }
499 }
500 }
501
502 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800503 * Synchronize the in-memory Intents with the Intents in the Intent
504 * framework.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800505 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800506 void synchronizeIntents() {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800507 synchronized (this) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800508
509 Map<IntentKey, Intent> localIntents = new HashMap<>();
510 Map<IntentKey, Intent> fetchedIntents = new HashMap<>();
511 Collection<Intent> storeInMemoryIntents = new LinkedList<>();
512 Collection<Intent> addIntents = new LinkedList<>();
513 Collection<Intent> deleteIntents = new LinkedList<>();
514
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800515 if (!isElectedLeader) {
516 return; // Nothing to do: not the leader anymore
517 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800518 log.debug("SDN-IP synchronizing all intents...");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800519
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800520 // Prepare the local intents
521 for (Intent intent : routeIntents.values()) {
522 localIntents.put(new IntentKey(intent), intent);
523 }
524 for (Intent intent : peerIntents.values()) {
525 localIntents.put(new IntentKey(intent), intent);
526 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800527
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800528 // Fetch all intents for this application
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800529 for (Intent intent : intentService.getIntents()) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800530 if (!intent.appId().equals(appId)) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800531 continue;
532 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800533 fetchedIntents.put(new IntentKey(intent), intent);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800534 }
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800535 if (log.isDebugEnabled()) {
536 for (Intent intent: fetchedIntents.values()) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800537 log.trace("SDN-IP Intent Synchronizer: fetched intent: {}",
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800538 intent);
539 }
540 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800541
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800542 computeIntentsDelta(localIntents, fetchedIntents,
543 storeInMemoryIntents, addIntents,
544 deleteIntents);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800545
546 //
547 // Perform the actions:
548 // 1. Store in memory fetched intents that are same. Can be done
549 // even if we are not the leader anymore
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800550 // 2. Delete intents: check if the leader before the operation
551 // 3. Add intents: check if the leader before the operation
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800552 //
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800553 for (Intent intent : storeInMemoryIntents) {
554 // Store the intent in memory based on its type
555 if (intent instanceof MultiPointToSinglePointIntent) {
556 MultiPointToSinglePointIntent mp2pIntent =
557 (MultiPointToSinglePointIntent) intent;
558 // Find the IP prefix
559 Criterion c =
560 mp2pIntent.selector().getCriterion(Criterion.Type.IPV4_DST);
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800561 if (c == null) {
562 // Try IPv6
563 c =
564 mp2pIntent.selector().getCriterion(Criterion.Type.IPV6_DST);
565 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800566 if (c != null && c instanceof IPCriterion) {
567 IPCriterion ipCriterion = (IPCriterion) c;
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800568 IpPrefix ipPrefix = ipCriterion.ip();
569 if (ipPrefix == null) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800570 continue;
571 }
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800572 log.trace("SDN-IP Intent Synchronizer: updating " +
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800573 "in-memory Route Intent for prefix {}",
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800574 ipPrefix);
575 routeIntents.put(ipPrefix, mp2pIntent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800576 } else {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800577 log.warn("SDN-IP no IPV4_DST or IPV6_DST criterion found for Intent {}",
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800578 mp2pIntent.id());
579 }
580 continue;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800581 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800582 if (intent instanceof PointToPointIntent) {
583 PointToPointIntent p2pIntent = (PointToPointIntent) intent;
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800584 log.trace("SDN-IP Intent Synchronizer: updating " +
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800585 "in-memory Peer Intent {}", p2pIntent);
586 peerIntents.put(new IntentKey(intent), p2pIntent);
587 continue;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800588 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800589 }
590
591 // Withdraw Intents
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800592 for (Intent intent : deleteIntents) {
Brian O'Connor03406a42015-02-03 17:28:57 -0800593 intentService.withdraw(intent);
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800594 log.trace("SDN-IP Intent Synchronizer: withdrawing intent: {}",
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800595 intent);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800596 }
597 if (!isElectedLeader) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800598 log.trace("SDN-IP Intent Synchronizer: cannot withdraw intents: " +
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800599 "not elected leader anymore");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800600 isActivatedLeader = false;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800601 return;
602 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800603
604 // Add Intents
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800605 for (Intent intent : addIntents) {
Brian O'Connor03406a42015-02-03 17:28:57 -0800606 intentService.submit(intent);
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800607 log.trace("SDN-IP Intent Synchronizer: submitting intent: {}",
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800608 intent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800609 }
610 if (!isElectedLeader) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800611 log.trace("SDN-IP Intent Synchronizer: cannot submit intents: " +
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800612 "not elected leader anymore");
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800613 isActivatedLeader = false;
614 return;
615 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800616
617 if (isElectedLeader) {
618 isActivatedLeader = true; // Allow push of Intents
619 } else {
620 isActivatedLeader = false;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800621 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800622 log.debug("SDN-IP intent synchronization completed");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800623 }
624 }
625
626 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800627 * Computes the delta in two sets of Intents: local in-memory Intents,
628 * and intents fetched from the Intent framework.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800629 *
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800630 * @param localIntents the local in-memory Intents
631 * @param fetchedIntents the Intents fetched from the Intent framework
632 * @param storeInMemoryIntents the Intents that should be stored in memory.
633 * Note: This Collection must be allocated by the caller, and it will
634 * be populated by this method.
635 * @param addIntents the Intents that should be added to the Intent
636 * framework. Note: This Collection must be allocated by the caller, and
637 * it will be populated by this method.
638 * @param deleteIntents the Intents that should be deleted from the Intent
639 * framework. Note: This Collection must be allocated by the caller, and
640 * it will be populated by this method.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800641 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800642 private void computeIntentsDelta(
643 final Map<IntentKey, Intent> localIntents,
644 final Map<IntentKey, Intent> fetchedIntents,
645 Collection<Intent> storeInMemoryIntents,
646 Collection<Intent> addIntents,
647 Collection<Intent> deleteIntents) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800648
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800649 //
650 // Compute the deltas between the LOCAL in-memory Intents and the
651 // FETCHED Intents:
652 // - If an Intent is in both the LOCAL and FETCHED sets:
653 // If the FETCHED Intent is WITHDRAWING or WITHDRAWN, then
654 // the LOCAL Intent should be added/installed; otherwise the
655 // FETCHED intent should be stored in the local memory
656 // (i.e., override the LOCAL Intent) to preserve the original
657 // Intent ID.
658 // - if a LOCAL Intent is not in the FETCHED set, then the LOCAL
659 // Intent should be added/installed.
660 // - If a FETCHED Intent is not in the LOCAL set, then the FETCHED
661 // Intent should be deleted/withdrawn.
662 //
663 for (Map.Entry<IntentKey, Intent> entry : localIntents.entrySet()) {
664 IntentKey intentKey = entry.getKey();
665 Intent localIntent = entry.getValue();
666 Intent fetchedIntent = fetchedIntents.get(intentKey);
667
668 if (fetchedIntent == null) {
669 //
670 // No FETCHED Intent found: push the LOCAL Intent.
671 //
672 addIntents.add(localIntent);
673 continue;
674 }
675
676 IntentState state =
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800677 intentService.getIntentState(fetchedIntent.key());
Pavlin Radoslavovdeb8a102014-11-26 13:31:36 -0800678 if (state == null ||
679 state == IntentState.WITHDRAWING ||
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800680 state == IntentState.WITHDRAWN) {
681 // The intent has been withdrawn but according to our route
682 // table it should be installed. We'll reinstall it.
683 addIntents.add(localIntent);
684 continue;
685 }
686 storeInMemoryIntents.add(fetchedIntent);
687 }
688
689 for (Map.Entry<IntentKey, Intent> entry : fetchedIntents.entrySet()) {
690 IntentKey intentKey = entry.getKey();
691 Intent fetchedIntent = entry.getValue();
692 Intent localIntent = localIntents.get(intentKey);
693
694 if (localIntent != null) {
695 continue;
696 }
697
698 IntentState state =
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800699 intentService.getIntentState(fetchedIntent.key());
Pavlin Radoslavovdeb8a102014-11-26 13:31:36 -0800700 if (state == null ||
701 state == IntentState.WITHDRAWING ||
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800702 state == IntentState.WITHDRAWN) {
703 // Nothing to do. The intent has been already withdrawn.
704 continue;
705 }
706 //
707 // No LOCAL Intent found: delete/withdraw the FETCHED Intent.
708 //
709 deleteIntents.add(fetchedIntent);
710 }
711 }
712
713 /**
714 * Helper class that can be used to compute the key for an Intent by
715 * by excluding the Intent ID.
716 */
717 static final class IntentKey {
718 private final Intent intent;
719
720 /**
721 * Constructor.
722 *
723 * @param intent the intent to use
724 */
725 IntentKey(Intent intent) {
726 checkArgument((intent instanceof MultiPointToSinglePointIntent) ||
727 (intent instanceof PointToPointIntent),
728 "Intent type not recognized", intent);
729 this.intent = intent;
730 }
731
732 /**
733 * Compares two Multi-Point to Single-Point Intents whether they
734 * represent same logical intention.
735 *
736 * @param intent1 the first Intent to compare
737 * @param intent2 the second Intent to compare
738 * @return true if both Intents represent same logical intention,
739 * otherwise false
740 */
741 static boolean equalIntents(MultiPointToSinglePointIntent intent1,
742 MultiPointToSinglePointIntent intent2) {
743 return Objects.equals(intent1.appId(), intent2.appId()) &&
744 Objects.equals(intent1.selector(), intent2.selector()) &&
745 Objects.equals(intent1.treatment(), intent2.treatment()) &&
746 Objects.equals(intent1.ingressPoints(), intent2.ingressPoints()) &&
747 Objects.equals(intent1.egressPoint(), intent2.egressPoint());
748 }
749
750 /**
751 * Compares two Point-to-Point Intents whether they represent
752 * same logical intention.
753 *
754 * @param intent1 the first Intent to compare
755 * @param intent2 the second Intent to compare
756 * @return true if both Intents represent same logical intention,
757 * otherwise false
758 */
759 static boolean equalIntents(PointToPointIntent intent1,
760 PointToPointIntent intent2) {
761 return Objects.equals(intent1.appId(), intent2.appId()) &&
762 Objects.equals(intent1.selector(), intent2.selector()) &&
763 Objects.equals(intent1.treatment(), intent2.treatment()) &&
764 Objects.equals(intent1.ingressPoint(), intent2.ingressPoint()) &&
765 Objects.equals(intent1.egressPoint(), intent2.egressPoint());
766 }
767
768 @Override
769 public int hashCode() {
770 if (intent instanceof PointToPointIntent) {
771 PointToPointIntent p2pIntent = (PointToPointIntent) intent;
772 return Objects.hash(p2pIntent.appId(),
773 p2pIntent.resources(),
774 p2pIntent.selector(),
775 p2pIntent.treatment(),
776 p2pIntent.constraints(),
777 p2pIntent.ingressPoint(),
778 p2pIntent.egressPoint());
779 }
780 if (intent instanceof MultiPointToSinglePointIntent) {
781 MultiPointToSinglePointIntent m2pIntent =
782 (MultiPointToSinglePointIntent) intent;
783 return Objects.hash(m2pIntent.appId(),
784 m2pIntent.resources(),
785 m2pIntent.selector(),
786 m2pIntent.treatment(),
787 m2pIntent.constraints(),
788 m2pIntent.ingressPoints(),
789 m2pIntent.egressPoint());
790 }
791 checkArgument(false, "Intent type not recognized", intent);
792 return 0;
793 }
794
795 @Override
796 public boolean equals(Object obj) {
797 if (this == obj) {
798 return true;
799 }
800 if ((obj == null) || (!(obj instanceof IntentKey))) {
801 return false;
802 }
803 IntentKey other = (IntentKey) obj;
804
805 if (this.intent instanceof PointToPointIntent) {
806 if (!(other.intent instanceof PointToPointIntent)) {
807 return false;
808 }
809 return equalIntents((PointToPointIntent) this.intent,
810 (PointToPointIntent) other.intent);
811 }
812 if (this.intent instanceof MultiPointToSinglePointIntent) {
813 if (!(other.intent instanceof MultiPointToSinglePointIntent)) {
814 return false;
815 }
816 return equalIntents(
817 (MultiPointToSinglePointIntent) this.intent,
818 (MultiPointToSinglePointIntent) other.intent);
819 }
820 checkArgument(false, "Intent type not recognized", intent);
821 return false;
822 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800823 }
Pingping Line28ae4c2015-03-13 11:37:03 -0700824
825 @Override
826 public void setUpConnectivityHostToHost(IpAddress dstIpAddress,
827 IpAddress srcIpAddress,
828 MacAddress srcMacAddress,
829 ConnectPoint srcConnectPoint) {
830 checkNotNull(dstIpAddress);
831 checkNotNull(srcIpAddress);
832 checkNotNull(srcMacAddress);
833 checkNotNull(srcConnectPoint);
834
835 IpPrefix srcIpPrefix = srcIpAddress.toIpPrefix();
836 IpPrefix dstIpPrefix = dstIpAddress.toIpPrefix();
837 ConnectPoint dstConnectPoint = null;
838 MacAddress dstMacAddress = null;
839
840 for (Host host : hostService.getHostsByIp(dstIpAddress)) {
841 if (host.mac() != null) {
842 dstMacAddress = host.mac();
843 dstConnectPoint = host.location();
844 break;
845 }
846 }
847 if (dstMacAddress == null) {
848 hostService.startMonitoringIp(dstIpAddress);
849 return;
850 }
851
852 //
853 // Handle intent from source host to destination host
854 //
855 MultiPointToSinglePointIntent srcToDstIntent =
856 hostToHostIntentGenerator(dstIpAddress, dstConnectPoint,
857 dstMacAddress, srcConnectPoint);
858 submitReactiveIntent(dstIpPrefix, srcToDstIntent);
859
860 //
861 // Handle intent from destination host to source host
862 //
863
864 // Since we proactively handle the intent from destination host to
865 // source host, we should check whether there is an exiting intent
866 // first.
867 if (mp2pIntentExists(srcIpPrefix)) {
868 updateExistingMp2pIntent(srcIpPrefix, dstConnectPoint);
869 return;
870 } else {
871 // There is no existing intent, create a new one.
872 MultiPointToSinglePointIntent dstToSrcIntent =
873 hostToHostIntentGenerator(srcIpAddress, srcConnectPoint,
874 srcMacAddress, dstConnectPoint);
875 submitReactiveIntent(srcIpPrefix, dstToSrcIntent);
876 }
877 }
878
879 /**
880 * Generates MultiPointToSinglePointIntent for both source host and
881 * destination host located in local SDN network.
882 *
883 * @param dstIpAddress the destination IP address
884 * @param dstConnectPoint the destination host connect point
885 * @param dstMacAddress the MAC address of destination host
886 * @param srcConnectPoint the connect point where packet-in from
887 * @return the generated MultiPointToSinglePointIntent
888 */
889 private MultiPointToSinglePointIntent hostToHostIntentGenerator(
890 IpAddress dstIpAddress,
891 ConnectPoint dstConnectPoint,
892 MacAddress dstMacAddress,
893 ConnectPoint srcConnectPoint) {
894 checkNotNull(dstIpAddress);
895 checkNotNull(dstConnectPoint);
896 checkNotNull(dstMacAddress);
897 checkNotNull(srcConnectPoint);
898
899 Set<ConnectPoint> ingressPoints = new HashSet<ConnectPoint>();
900 ingressPoints.add(srcConnectPoint);
901 IpPrefix dstIpPrefix = dstIpAddress.toIpPrefix();
902
903 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
904 if (dstIpAddress.isIp4()) {
905 selector.matchEthType(Ethernet.TYPE_IPV4);
906 selector.matchIPDst(dstIpPrefix);
907 } else {
908 selector.matchEthType(Ethernet.TYPE_IPV6);
909 selector.matchIPv6Dst(dstIpPrefix);
910 }
911
912 // Rewrite the destination MAC address
913 TrafficTreatment.Builder treatment =
914 DefaultTrafficTreatment.builder().setEthDst(dstMacAddress);
915
916 Key key = Key.of(dstIpPrefix.toString(), appId);
917 int priority = dstIpPrefix.prefixLength() * PRIORITY_MULTIPLIER
918 + PRIORITY_OFFSET;
919 MultiPointToSinglePointIntent intent =
920 MultiPointToSinglePointIntent.builder()
921 .appId(appId)
922 .key(key)
923 .selector(selector.build())
924 .treatment(treatment.build())
925 .ingressPoints(ingressPoints)
926 .egressPoint(dstConnectPoint)
927 .priority(priority)
928 .build();
929
930 log.trace("Generates ConnectivityHostToHost = {} ", intent);
931 return intent;
932 }
933
934 @Override
935 public void updateExistingMp2pIntent(IpPrefix ipPrefix,
936 ConnectPoint ingressConnectPoint) {
937 checkNotNull(ipPrefix);
938 checkNotNull(ingressConnectPoint);
939
940 MultiPointToSinglePointIntent existingIntent =
941 getExistingMp2pIntent(ipPrefix);
942 if (existingIntent != null) {
943 Set<ConnectPoint> ingressPoints = existingIntent.ingressPoints();
944 // Add host connect point into ingressPoints of the existing intent
945 if (ingressPoints.add(ingressConnectPoint)) {
946 MultiPointToSinglePointIntent updatedMp2pIntent =
947 MultiPointToSinglePointIntent.builder()
948 .appId(appId)
949 .key(existingIntent.key())
950 .selector(existingIntent.selector())
951 .treatment(existingIntent.treatment())
952 .ingressPoints(ingressPoints)
953 .egressPoint(existingIntent.egressPoint())
954 .priority(existingIntent.priority())
955 .build();
956
957 log.trace("Update an existing MultiPointToSinglePointIntent "
958 + "to new intent = {} ", updatedMp2pIntent);
959 submitReactiveIntent(ipPrefix, updatedMp2pIntent);
960 }
961 // If adding ingressConnectPoint to ingressPoints failed, it
962 // because between the time interval from checking existing intent
963 // to generating new intent, onos updated this intent due to other
964 // packet-in and the new intent also includes the
965 // ingressConnectPoint. This will not affect reactive routing.
966 }
967 }
968
969 @Override
970 public boolean mp2pIntentExists(IpPrefix ipPrefix) {
971 checkNotNull(ipPrefix);
972 return routeIntents.get(ipPrefix) == null ? false : true;
973 }
974
975 /**
976 * Gets the existing MultiPointToSinglePointIntent from memory for a given
977 * IP prefix.
978 *
979 * @param ipPrefix the IP prefix used to find MultiPointToSinglePointIntent
980 * @return the MultiPointToSinglePointIntent if found, otherwise null
981 */
982 private MultiPointToSinglePointIntent getExistingMp2pIntent(IpPrefix
983 ipPrefix) {
984 checkNotNull(ipPrefix);
985 return routeIntents.get(ipPrefix);
986 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800987}