blob: cad87527684cd4f177efaf411d97fe0f3bb7bb96 [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;
Sho SHIMIZUfbc80e52015-04-28 10:41:58 -070043import org.onosproject.net.flow.criteria.IPCriterion;
Brian O'Connorabafb502014-12-02 22:26:20 -080044import 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);
Pingping Lin8a524712015-06-24 14:58:24 -0700384 Set<ConnectPoint> ingressPoints =
385 configService.getBgpPeerConnectPoints();
386
Pingping Line28ae4c2015-03-13 11:37:03 -0700387 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
388
389 if (hostIpAddress.isIp4()) {
390 selector.matchEthType(Ethernet.TYPE_IPV4);
391 } else {
392 selector.matchEthType(Ethernet.TYPE_IPV6);
393 }
394
395 // Match the destination IP prefix at the first hop
396 IpPrefix ipPrefix = hostIpAddress.toIpPrefix();
397 selector.matchIPDst(ipPrefix);
398
399 // Rewrite the destination MAC address
400 MacAddress hostMac = null;
401 ConnectPoint egressPoint = null;
402 for (Host host : hostService.getHostsByIp(hostIpAddress)) {
403 if (host.mac() != null) {
404 hostMac = host.mac();
405 egressPoint = host.location();
406 break;
407 }
408 }
409 if (hostMac == null) {
410 hostService.startMonitoringIp(hostIpAddress);
411 return;
412 }
413
414 TrafficTreatment.Builder treatment =
415 DefaultTrafficTreatment.builder().setEthDst(hostMac);
416 Key key = Key.of(ipPrefix.toString(), appId);
417 int priority = ipPrefix.prefixLength() * PRIORITY_MULTIPLIER
418 + PRIORITY_OFFSET;
419 MultiPointToSinglePointIntent intent =
420 MultiPointToSinglePointIntent.builder()
421 .appId(appId)
422 .key(key)
423 .selector(selector.build())
424 .treatment(treatment.build())
425 .ingressPoints(ingressPoints)
426 .egressPoint(egressPoint)
427 .priority(priority)
428 .build();
429
430 log.trace("Generates ConnectivityInternetToHost intent {}", intent);
431 submitReactiveIntent(ipPrefix, intent);
432 }
433
434
435 @Override
Jonathan Hart552e31f2015-02-06 11:11:59 -0800436 public void update(Collection<FibUpdate> updates, Collection<FibUpdate> withdraws) {
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800437 //
438 // NOTE: Semantically, we MUST withdraw existing intents before
439 // submitting new intents.
440 //
441 synchronized (this) {
442 MultiPointToSinglePointIntent intent;
443
444 log.debug("SDN-IP submitting intents = {} withdrawing = {}",
Jonathan Hart552e31f2015-02-06 11:11:59 -0800445 updates.size(), withdraws.size());
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800446
447 //
448 // Prepare the Intent batch operations for the intents to withdraw
449 //
Jonathan Hart552e31f2015-02-06 11:11:59 -0800450 for (FibUpdate withdraw : withdraws) {
451 checkArgument(withdraw.type() == FibUpdate.Type.DELETE,
452 "FibUpdate with wrong type in withdraws list");
453
454 IpPrefix prefix = withdraw.entry().prefix();
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800455 intent = routeIntents.remove(prefix);
456 if (intent == null) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800457 log.trace("SDN-IP No intent in routeIntents to delete " +
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800458 "for prefix: {}", prefix);
459 continue;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800460 }
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800461 if (isElectedLeader && isActivatedLeader) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800462 log.trace("SDN-IP Withdrawing intent: {}", intent);
Brian O'Connor03406a42015-02-03 17:28:57 -0800463 intentService.withdraw(intent);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800464 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800465 }
466
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800467 //
468 // Prepare the Intent batch operations for the intents to submit
469 //
Jonathan Hart552e31f2015-02-06 11:11:59 -0800470 for (FibUpdate update : updates) {
471 checkArgument(update.type() == FibUpdate.Type.UPDATE,
472 "FibUpdate with wrong type in updates list");
473
474 IpPrefix prefix = update.entry().prefix();
475 intent = generateRouteIntent(prefix, update.entry().nextHopIp(),
476 update.entry().nextHopMac());
477
478 if (intent == null) {
479 // This preserves the old semantics - if an intent can't be
480 // generated, we don't do anything with that prefix. But
481 // perhaps we should withdraw the old intent anyway?
482 continue;
483 }
484
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800485 MultiPointToSinglePointIntent oldIntent =
486 routeIntents.put(prefix, intent);
487 if (isElectedLeader && isActivatedLeader) {
488 if (oldIntent != null) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800489 log.trace("SDN-IP Withdrawing old intent: {}",
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800490 oldIntent);
Brian O'Connor03406a42015-02-03 17:28:57 -0800491 intentService.withdraw(oldIntent);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800492 }
Jonathan Hartb3b8a0a2015-02-12 17:36:13 -0800493 log.trace("SDN-IP Submitting intent: {}", intent);
494 intentService.submit(intent);
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800495 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800496 }
497 }
498 }
499
500 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800501 * Synchronize the in-memory Intents with the Intents in the Intent
502 * framework.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800503 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800504 void synchronizeIntents() {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800505 synchronized (this) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800506
507 Map<IntentKey, Intent> localIntents = new HashMap<>();
508 Map<IntentKey, Intent> fetchedIntents = new HashMap<>();
509 Collection<Intent> storeInMemoryIntents = new LinkedList<>();
510 Collection<Intent> addIntents = new LinkedList<>();
511 Collection<Intent> deleteIntents = new LinkedList<>();
512
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800513 if (!isElectedLeader) {
514 return; // Nothing to do: not the leader anymore
515 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800516 log.debug("SDN-IP synchronizing all intents...");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800517
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800518 // Prepare the local intents
519 for (Intent intent : routeIntents.values()) {
520 localIntents.put(new IntentKey(intent), intent);
521 }
522 for (Intent intent : peerIntents.values()) {
523 localIntents.put(new IntentKey(intent), intent);
524 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800525
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800526 // Fetch all intents for this application
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800527 for (Intent intent : intentService.getIntents()) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800528 if (!intent.appId().equals(appId)) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800529 continue;
530 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800531 fetchedIntents.put(new IntentKey(intent), intent);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800532 }
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800533 if (log.isDebugEnabled()) {
534 for (Intent intent: fetchedIntents.values()) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800535 log.trace("SDN-IP Intent Synchronizer: fetched intent: {}",
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800536 intent);
537 }
538 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800539
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800540 computeIntentsDelta(localIntents, fetchedIntents,
541 storeInMemoryIntents, addIntents,
542 deleteIntents);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800543
544 //
545 // Perform the actions:
546 // 1. Store in memory fetched intents that are same. Can be done
547 // even if we are not the leader anymore
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800548 // 2. Delete intents: check if the leader before the operation
549 // 3. Add intents: check if the leader before the operation
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800550 //
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800551 for (Intent intent : storeInMemoryIntents) {
552 // Store the intent in memory based on its type
553 if (intent instanceof MultiPointToSinglePointIntent) {
554 MultiPointToSinglePointIntent mp2pIntent =
555 (MultiPointToSinglePointIntent) intent;
556 // Find the IP prefix
557 Criterion c =
558 mp2pIntent.selector().getCriterion(Criterion.Type.IPV4_DST);
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800559 if (c == null) {
560 // Try IPv6
561 c =
562 mp2pIntent.selector().getCriterion(Criterion.Type.IPV6_DST);
563 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800564 if (c != null && c instanceof IPCriterion) {
565 IPCriterion ipCriterion = (IPCriterion) c;
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800566 IpPrefix ipPrefix = ipCriterion.ip();
567 if (ipPrefix == null) {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800568 continue;
569 }
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800570 log.trace("SDN-IP Intent Synchronizer: updating " +
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800571 "in-memory Route Intent for prefix {}",
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800572 ipPrefix);
573 routeIntents.put(ipPrefix, mp2pIntent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800574 } else {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800575 log.warn("SDN-IP no IPV4_DST or IPV6_DST criterion found for Intent {}",
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800576 mp2pIntent.id());
577 }
578 continue;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800579 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800580 if (intent instanceof PointToPointIntent) {
581 PointToPointIntent p2pIntent = (PointToPointIntent) intent;
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800582 log.trace("SDN-IP Intent Synchronizer: updating " +
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800583 "in-memory Peer Intent {}", p2pIntent);
584 peerIntents.put(new IntentKey(intent), p2pIntent);
585 continue;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800586 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800587 }
588
589 // Withdraw Intents
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800590 for (Intent intent : deleteIntents) {
Brian O'Connor03406a42015-02-03 17:28:57 -0800591 intentService.withdraw(intent);
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800592 log.trace("SDN-IP Intent Synchronizer: withdrawing intent: {}",
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800593 intent);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800594 }
595 if (!isElectedLeader) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800596 log.trace("SDN-IP Intent Synchronizer: cannot withdraw intents: " +
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800597 "not elected leader anymore");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800598 isActivatedLeader = false;
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800599 return;
600 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800601
602 // Add Intents
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800603 for (Intent intent : addIntents) {
Brian O'Connor03406a42015-02-03 17:28:57 -0800604 intentService.submit(intent);
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800605 log.trace("SDN-IP Intent Synchronizer: submitting intent: {}",
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800606 intent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800607 }
608 if (!isElectedLeader) {
Pavlin Radoslavov8049bb82014-12-02 13:58:35 -0800609 log.trace("SDN-IP Intent Synchronizer: cannot submit intents: " +
Pavlin Radoslavovcaf63372014-11-26 11:59:11 -0800610 "not elected leader anymore");
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800611 isActivatedLeader = false;
612 return;
613 }
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800614
615 if (isElectedLeader) {
616 isActivatedLeader = true; // Allow push of Intents
617 } else {
618 isActivatedLeader = false;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800619 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800620 log.debug("SDN-IP intent synchronization completed");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800621 }
622 }
623
624 /**
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800625 * Computes the delta in two sets of Intents: local in-memory Intents,
626 * and intents fetched from the Intent framework.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800627 *
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800628 * @param localIntents the local in-memory Intents
629 * @param fetchedIntents the Intents fetched from the Intent framework
630 * @param storeInMemoryIntents the Intents that should be stored in memory.
631 * Note: This Collection must be allocated by the caller, and it will
632 * be populated by this method.
633 * @param addIntents the Intents that should be added to the Intent
634 * framework. Note: This Collection must be allocated by the caller, and
635 * it will be populated by this method.
636 * @param deleteIntents the Intents that should be deleted from the Intent
637 * framework. Note: This Collection must be allocated by the caller, and
638 * it will be populated by this method.
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800639 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800640 private void computeIntentsDelta(
641 final Map<IntentKey, Intent> localIntents,
642 final Map<IntentKey, Intent> fetchedIntents,
643 Collection<Intent> storeInMemoryIntents,
644 Collection<Intent> addIntents,
645 Collection<Intent> deleteIntents) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800646
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800647 //
648 // Compute the deltas between the LOCAL in-memory Intents and the
649 // FETCHED Intents:
650 // - If an Intent is in both the LOCAL and FETCHED sets:
651 // If the FETCHED Intent is WITHDRAWING or WITHDRAWN, then
652 // the LOCAL Intent should be added/installed; otherwise the
653 // FETCHED intent should be stored in the local memory
654 // (i.e., override the LOCAL Intent) to preserve the original
655 // Intent ID.
656 // - if a LOCAL Intent is not in the FETCHED set, then the LOCAL
657 // Intent should be added/installed.
658 // - If a FETCHED Intent is not in the LOCAL set, then the FETCHED
659 // Intent should be deleted/withdrawn.
660 //
661 for (Map.Entry<IntentKey, Intent> entry : localIntents.entrySet()) {
662 IntentKey intentKey = entry.getKey();
663 Intent localIntent = entry.getValue();
664 Intent fetchedIntent = fetchedIntents.get(intentKey);
665
666 if (fetchedIntent == null) {
667 //
668 // No FETCHED Intent found: push the LOCAL Intent.
669 //
670 addIntents.add(localIntent);
671 continue;
672 }
673
674 IntentState state =
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800675 intentService.getIntentState(fetchedIntent.key());
Pavlin Radoslavovdeb8a102014-11-26 13:31:36 -0800676 if (state == null ||
677 state == IntentState.WITHDRAWING ||
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800678 state == IntentState.WITHDRAWN) {
679 // The intent has been withdrawn but according to our route
680 // table it should be installed. We'll reinstall it.
681 addIntents.add(localIntent);
682 continue;
683 }
684 storeInMemoryIntents.add(fetchedIntent);
685 }
686
687 for (Map.Entry<IntentKey, Intent> entry : fetchedIntents.entrySet()) {
688 IntentKey intentKey = entry.getKey();
689 Intent fetchedIntent = entry.getValue();
690 Intent localIntent = localIntents.get(intentKey);
691
692 if (localIntent != null) {
693 continue;
694 }
695
696 IntentState state =
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800697 intentService.getIntentState(fetchedIntent.key());
Pavlin Radoslavovdeb8a102014-11-26 13:31:36 -0800698 if (state == null ||
699 state == IntentState.WITHDRAWING ||
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800700 state == IntentState.WITHDRAWN) {
701 // Nothing to do. The intent has been already withdrawn.
702 continue;
703 }
704 //
705 // No LOCAL Intent found: delete/withdraw the FETCHED Intent.
706 //
707 deleteIntents.add(fetchedIntent);
708 }
709 }
710
711 /**
712 * Helper class that can be used to compute the key for an Intent by
713 * by excluding the Intent ID.
714 */
715 static final class IntentKey {
716 private final Intent intent;
717
718 /**
719 * Constructor.
720 *
721 * @param intent the intent to use
722 */
723 IntentKey(Intent intent) {
724 checkArgument((intent instanceof MultiPointToSinglePointIntent) ||
725 (intent instanceof PointToPointIntent),
726 "Intent type not recognized", intent);
727 this.intent = intent;
728 }
729
730 /**
731 * Compares two Multi-Point to Single-Point Intents whether they
732 * represent same logical intention.
733 *
734 * @param intent1 the first Intent to compare
735 * @param intent2 the second Intent to compare
736 * @return true if both Intents represent same logical intention,
737 * otherwise false
738 */
739 static boolean equalIntents(MultiPointToSinglePointIntent intent1,
740 MultiPointToSinglePointIntent intent2) {
741 return Objects.equals(intent1.appId(), intent2.appId()) &&
742 Objects.equals(intent1.selector(), intent2.selector()) &&
743 Objects.equals(intent1.treatment(), intent2.treatment()) &&
744 Objects.equals(intent1.ingressPoints(), intent2.ingressPoints()) &&
745 Objects.equals(intent1.egressPoint(), intent2.egressPoint());
746 }
747
748 /**
749 * Compares two Point-to-Point Intents whether they represent
750 * same logical intention.
751 *
752 * @param intent1 the first Intent to compare
753 * @param intent2 the second Intent to compare
754 * @return true if both Intents represent same logical intention,
755 * otherwise false
756 */
757 static boolean equalIntents(PointToPointIntent intent1,
758 PointToPointIntent intent2) {
759 return Objects.equals(intent1.appId(), intent2.appId()) &&
760 Objects.equals(intent1.selector(), intent2.selector()) &&
761 Objects.equals(intent1.treatment(), intent2.treatment()) &&
762 Objects.equals(intent1.ingressPoint(), intent2.ingressPoint()) &&
763 Objects.equals(intent1.egressPoint(), intent2.egressPoint());
764 }
765
766 @Override
767 public int hashCode() {
768 if (intent instanceof PointToPointIntent) {
769 PointToPointIntent p2pIntent = (PointToPointIntent) intent;
770 return Objects.hash(p2pIntent.appId(),
771 p2pIntent.resources(),
772 p2pIntent.selector(),
773 p2pIntent.treatment(),
774 p2pIntent.constraints(),
775 p2pIntent.ingressPoint(),
776 p2pIntent.egressPoint());
777 }
778 if (intent instanceof MultiPointToSinglePointIntent) {
779 MultiPointToSinglePointIntent m2pIntent =
780 (MultiPointToSinglePointIntent) intent;
781 return Objects.hash(m2pIntent.appId(),
782 m2pIntent.resources(),
783 m2pIntent.selector(),
784 m2pIntent.treatment(),
785 m2pIntent.constraints(),
786 m2pIntent.ingressPoints(),
787 m2pIntent.egressPoint());
788 }
789 checkArgument(false, "Intent type not recognized", intent);
790 return 0;
791 }
792
793 @Override
794 public boolean equals(Object obj) {
795 if (this == obj) {
796 return true;
797 }
798 if ((obj == null) || (!(obj instanceof IntentKey))) {
799 return false;
800 }
801 IntentKey other = (IntentKey) obj;
802
803 if (this.intent instanceof PointToPointIntent) {
804 if (!(other.intent instanceof PointToPointIntent)) {
805 return false;
806 }
807 return equalIntents((PointToPointIntent) this.intent,
808 (PointToPointIntent) other.intent);
809 }
810 if (this.intent instanceof MultiPointToSinglePointIntent) {
811 if (!(other.intent instanceof MultiPointToSinglePointIntent)) {
812 return false;
813 }
814 return equalIntents(
815 (MultiPointToSinglePointIntent) this.intent,
816 (MultiPointToSinglePointIntent) other.intent);
817 }
818 checkArgument(false, "Intent type not recognized", intent);
819 return false;
820 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800821 }
Pingping Line28ae4c2015-03-13 11:37:03 -0700822
823 @Override
824 public void setUpConnectivityHostToHost(IpAddress dstIpAddress,
825 IpAddress srcIpAddress,
826 MacAddress srcMacAddress,
827 ConnectPoint srcConnectPoint) {
828 checkNotNull(dstIpAddress);
829 checkNotNull(srcIpAddress);
830 checkNotNull(srcMacAddress);
831 checkNotNull(srcConnectPoint);
832
833 IpPrefix srcIpPrefix = srcIpAddress.toIpPrefix();
834 IpPrefix dstIpPrefix = dstIpAddress.toIpPrefix();
835 ConnectPoint dstConnectPoint = null;
836 MacAddress dstMacAddress = null;
837
838 for (Host host : hostService.getHostsByIp(dstIpAddress)) {
839 if (host.mac() != null) {
840 dstMacAddress = host.mac();
841 dstConnectPoint = host.location();
842 break;
843 }
844 }
845 if (dstMacAddress == null) {
846 hostService.startMonitoringIp(dstIpAddress);
847 return;
848 }
849
850 //
851 // Handle intent from source host to destination host
852 //
853 MultiPointToSinglePointIntent srcToDstIntent =
854 hostToHostIntentGenerator(dstIpAddress, dstConnectPoint,
855 dstMacAddress, srcConnectPoint);
856 submitReactiveIntent(dstIpPrefix, srcToDstIntent);
857
858 //
859 // Handle intent from destination host to source host
860 //
861
862 // Since we proactively handle the intent from destination host to
863 // source host, we should check whether there is an exiting intent
864 // first.
865 if (mp2pIntentExists(srcIpPrefix)) {
866 updateExistingMp2pIntent(srcIpPrefix, dstConnectPoint);
867 return;
868 } else {
869 // There is no existing intent, create a new one.
870 MultiPointToSinglePointIntent dstToSrcIntent =
871 hostToHostIntentGenerator(srcIpAddress, srcConnectPoint,
872 srcMacAddress, dstConnectPoint);
873 submitReactiveIntent(srcIpPrefix, dstToSrcIntent);
874 }
875 }
876
877 /**
878 * Generates MultiPointToSinglePointIntent for both source host and
879 * destination host located in local SDN network.
880 *
881 * @param dstIpAddress the destination IP address
882 * @param dstConnectPoint the destination host connect point
883 * @param dstMacAddress the MAC address of destination host
884 * @param srcConnectPoint the connect point where packet-in from
885 * @return the generated MultiPointToSinglePointIntent
886 */
887 private MultiPointToSinglePointIntent hostToHostIntentGenerator(
888 IpAddress dstIpAddress,
889 ConnectPoint dstConnectPoint,
890 MacAddress dstMacAddress,
891 ConnectPoint srcConnectPoint) {
892 checkNotNull(dstIpAddress);
893 checkNotNull(dstConnectPoint);
894 checkNotNull(dstMacAddress);
895 checkNotNull(srcConnectPoint);
896
897 Set<ConnectPoint> ingressPoints = new HashSet<ConnectPoint>();
898 ingressPoints.add(srcConnectPoint);
899 IpPrefix dstIpPrefix = dstIpAddress.toIpPrefix();
900
901 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
902 if (dstIpAddress.isIp4()) {
903 selector.matchEthType(Ethernet.TYPE_IPV4);
904 selector.matchIPDst(dstIpPrefix);
905 } else {
906 selector.matchEthType(Ethernet.TYPE_IPV6);
907 selector.matchIPv6Dst(dstIpPrefix);
908 }
909
910 // Rewrite the destination MAC address
911 TrafficTreatment.Builder treatment =
912 DefaultTrafficTreatment.builder().setEthDst(dstMacAddress);
913
914 Key key = Key.of(dstIpPrefix.toString(), appId);
915 int priority = dstIpPrefix.prefixLength() * PRIORITY_MULTIPLIER
916 + PRIORITY_OFFSET;
917 MultiPointToSinglePointIntent intent =
918 MultiPointToSinglePointIntent.builder()
919 .appId(appId)
920 .key(key)
921 .selector(selector.build())
922 .treatment(treatment.build())
923 .ingressPoints(ingressPoints)
924 .egressPoint(dstConnectPoint)
925 .priority(priority)
926 .build();
927
928 log.trace("Generates ConnectivityHostToHost = {} ", intent);
929 return intent;
930 }
931
932 @Override
933 public void updateExistingMp2pIntent(IpPrefix ipPrefix,
934 ConnectPoint ingressConnectPoint) {
935 checkNotNull(ipPrefix);
936 checkNotNull(ingressConnectPoint);
937
938 MultiPointToSinglePointIntent existingIntent =
939 getExistingMp2pIntent(ipPrefix);
940 if (existingIntent != null) {
941 Set<ConnectPoint> ingressPoints = existingIntent.ingressPoints();
942 // Add host connect point into ingressPoints of the existing intent
943 if (ingressPoints.add(ingressConnectPoint)) {
944 MultiPointToSinglePointIntent updatedMp2pIntent =
945 MultiPointToSinglePointIntent.builder()
946 .appId(appId)
947 .key(existingIntent.key())
948 .selector(existingIntent.selector())
949 .treatment(existingIntent.treatment())
950 .ingressPoints(ingressPoints)
951 .egressPoint(existingIntent.egressPoint())
952 .priority(existingIntent.priority())
953 .build();
954
955 log.trace("Update an existing MultiPointToSinglePointIntent "
956 + "to new intent = {} ", updatedMp2pIntent);
957 submitReactiveIntent(ipPrefix, updatedMp2pIntent);
958 }
959 // If adding ingressConnectPoint to ingressPoints failed, it
960 // because between the time interval from checking existing intent
961 // to generating new intent, onos updated this intent due to other
962 // packet-in and the new intent also includes the
963 // ingressConnectPoint. This will not affect reactive routing.
964 }
965 }
966
967 @Override
968 public boolean mp2pIntentExists(IpPrefix ipPrefix) {
969 checkNotNull(ipPrefix);
Sho SHIMIZUfe89d3a2015-06-30 18:15:39 -0700970 return routeIntents.get(ipPrefix) != null;
Pingping Line28ae4c2015-03-13 11:37:03 -0700971 }
972
973 /**
974 * Gets the existing MultiPointToSinglePointIntent from memory for a given
975 * IP prefix.
976 *
977 * @param ipPrefix the IP prefix used to find MultiPointToSinglePointIntent
978 * @return the MultiPointToSinglePointIntent if found, otherwise null
979 */
980 private MultiPointToSinglePointIntent getExistingMp2pIntent(IpPrefix
981 ipPrefix) {
982 checkNotNull(ipPrefix);
983 return routeIntents.get(ipPrefix);
984 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800985}