diff options
author | Ashlee Young <ashlee@onosfw.com> | 2015-10-09 18:32:44 -0700 |
---|---|---|
committer | Ashlee Young <ashlee@onosfw.com> | 2015-10-09 18:32:44 -0700 |
commit | 6a07d2d622eaa06953f3353e39c080984076e8de (patch) | |
tree | bfb50a2090fce186c2cc545a400c969bf2ea702b /framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java | |
parent | e6d71622143ff9b2421a1abbe8434b954b5b1099 (diff) |
Updated master to commit id 6ee8aa3e67ce89908a8c93aa9445c6f71a18f986
Change-Id: I94b055ee2f298daf71e2ec794fd0f2495bd8081f
Diffstat (limited to 'framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java')
-rw-r--r-- | framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java | 901 |
1 files changed, 93 insertions, 808 deletions
diff --git a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java index d8d8f45d..eaabed33 100644 --- a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java +++ b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java @@ -15,118 +15,79 @@ */ package org.onosproject.sdnip; -import com.google.common.collect.ImmutableList; -import com.google.common.util.concurrent.ThreadFactoryBuilder; -import org.onlab.packet.Ethernet; -import org.onlab.packet.IpAddress; -import org.onlab.packet.IpPrefix; -import org.onlab.packet.MacAddress; -import org.onlab.packet.VlanId; import org.onosproject.core.ApplicationId; -import org.onosproject.incubator.net.intf.Interface; -import org.onosproject.incubator.net.intf.InterfaceService; -import org.onosproject.net.ConnectPoint; -import org.onosproject.net.Host; -import org.onosproject.net.flow.DefaultTrafficSelector; -import org.onosproject.net.flow.DefaultTrafficTreatment; -import org.onosproject.net.flow.TrafficSelector; -import org.onosproject.net.flow.TrafficTreatment; -import org.onosproject.net.flow.criteria.Criterion; -import org.onosproject.net.flow.criteria.IPCriterion; -import org.onosproject.net.host.HostService; -import org.onosproject.net.intent.Constraint; import org.onosproject.net.intent.Intent; import org.onosproject.net.intent.IntentService; import org.onosproject.net.intent.IntentState; import org.onosproject.net.intent.Key; -import org.onosproject.net.intent.MultiPointToSinglePointIntent; -import org.onosproject.net.intent.PointToPointIntent; -import org.onosproject.net.intent.constraint.PartialFailureConstraint; -import org.onosproject.routing.FibListener; -import org.onosproject.routing.FibUpdate; -import org.onosproject.routing.IntentRequestListener; -import org.onosproject.routing.config.RoutingConfigurationService; +import org.onosproject.routing.IntentSynchronizationService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Collection; import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Objects; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Semaphore; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.concurrent.Executors.newSingleThreadExecutor; +import static org.onlab.util.Tools.groupedThreads; /** * Synchronizes intents between the in-memory intent store and the * IntentService. */ -public class IntentSynchronizer implements FibListener, IntentRequestListener { - private static final int PRIORITY_OFFSET = 100; - private static final int PRIORITY_MULTIPLIER = 5; - protected static final ImmutableList<Constraint> CONSTRAINTS - = ImmutableList.of(new PartialFailureConstraint()); +public class IntentSynchronizer implements IntentSynchronizationService { private static final Logger log = LoggerFactory.getLogger(IntentSynchronizer.class); private final ApplicationId appId; private final IntentService intentService; - private final HostService hostService; - private final InterfaceService interfaceService; - private final Map<IntentKey, PointToPointIntent> peerIntents; - private final Map<IpPrefix, MultiPointToSinglePointIntent> routeIntents; + + private final Map<Key, Intent> intents; // // State to deal with SDN-IP Leader election and pushing Intents // private final ExecutorService bgpIntentsSynchronizerExecutor; - private final Semaphore intentsSynchronizerSemaphore = new Semaphore(0); private volatile boolean isElectedLeader = false; private volatile boolean isActivatedLeader = false; - private final RoutingConfigurationService configService; + /** + * Class constructor. + * + * @param appId the Application ID + * @param intentService the intent service + */ + IntentSynchronizer(ApplicationId appId, IntentService intentService) { + this(appId, intentService, + newSingleThreadExecutor(groupedThreads("onos/sdnip", "sync"))); + } /** * Class constructor. * * @param appId the Application ID * @param intentService the intent service - * @param hostService the host service - * @param configService the SDN-IP configuration service - * @param interfaceService the interface service + * @param executorService executor service for synchronization thread */ IntentSynchronizer(ApplicationId appId, IntentService intentService, - HostService hostService, - RoutingConfigurationService configService, - InterfaceService interfaceService) { + ExecutorService executorService) { this.appId = appId; this.intentService = intentService; - this.hostService = hostService; - this.interfaceService = interfaceService; - peerIntents = new ConcurrentHashMap<>(); - routeIntents = new ConcurrentHashMap<>(); - this.configService = configService; + intents = new ConcurrentHashMap<>(); - bgpIntentsSynchronizerExecutor = Executors.newSingleThreadExecutor( - new ThreadFactoryBuilder() - .setNameFormat("sdnip-intents-synchronizer-%d").build()); + bgpIntentsSynchronizerExecutor = executorService; } /** * Starts the synchronizer. */ public void start() { - bgpIntentsSynchronizerExecutor.execute(this::doIntentSynchronizationThread); + } /** @@ -187,794 +148,118 @@ public class IntentSynchronizer implements FibListener, IntentRequestListener { } } - /** - * Signals the synchronizer that the SDN-IP leadership has changed. - * - * @param isLeader true if this instance is now the leader, otherwise false - */ - public void leaderChanged(boolean isLeader) { - log.debug("SDN-IP Leader changed: {}", isLeader); - - if (!isLeader) { - this.isElectedLeader = false; - this.isActivatedLeader = false; - return; // Nothing to do - } - this.isActivatedLeader = false; - this.isElectedLeader = true; - - // - // Tell the Intents Synchronizer thread to start the synchronization - // - intentsSynchronizerSemaphore.release(); - } - - /** - * Gets the route intents. - * - * @return the route intents - */ - public Collection<MultiPointToSinglePointIntent> getRouteIntents() { - List<MultiPointToSinglePointIntent> result = new LinkedList<>(); - - for (Map.Entry<IpPrefix, MultiPointToSinglePointIntent> entry : - routeIntents.entrySet()) { - result.add(entry.getValue()); - } - return result; - } - - /** - * Thread for Intent Synchronization. - */ - private void doIntentSynchronizationThread() { - boolean interrupted = false; - try { - while (!interrupted) { - try { - intentsSynchronizerSemaphore.acquire(); - // - // Drain all permits, because a single synchronization is - // sufficient. - // - intentsSynchronizerSemaphore.drainPermits(); - } catch (InterruptedException e) { - interrupted = true; - break; - } - synchronizeIntents(); - } - } finally { - if (interrupted) { - Thread.currentThread().interrupt(); - } - } - } - - /** - * Submits a collection of point-to-point intents. - * - * @param intents the intents to submit - */ - void submitPeerIntents(Collection<PointToPointIntent> intents) { + @Override + public void submit(Intent intent) { synchronized (this) { - // Store the intents in memory - for (PointToPointIntent intent : intents) { - peerIntents.put(new IntentKey(intent), intent); - } - - // Push the intents + intents.put(intent.key(), intent); if (isElectedLeader && isActivatedLeader) { - log.debug("SDN-IP Submitting all Peer Intents..."); - for (Intent intent : intents) { - log.trace("SDN-IP Submitting intents: {}", intent); - intentService.submit(intent); - } + log.trace("SDN-IP Submitting intent: {}", intent); + intentService.submit(intent); } } } - /** - * Submits a MultiPointToSinglePointIntent for reactive routing. - * - * @param ipPrefix the IP prefix to match in a MultiPointToSinglePointIntent - * @param intent the intent to submit - */ - void submitReactiveIntent(IpPrefix ipPrefix, MultiPointToSinglePointIntent intent) { + @Override + public void withdraw(Intent intent) { synchronized (this) { - // Store the intent in memory - routeIntents.put(ipPrefix, intent); - - // Push the intent + intents.remove(intent.key(), intent); if (isElectedLeader && isActivatedLeader) { - log.trace("SDN-IP submitting reactive routing intent: {}", intent); - intentService.submit(intent); + log.trace("SDN-IP Withdrawing intent: {}", intent); + intentService.withdraw(intent); } } } /** - * Generates a route intent for a prefix, the next hop IP address, and - * the next hop MAC address. - * <p/> - * This method will find the egress interface for the intent. - * Intent will match dst IP prefix and rewrite dst MAC address at all other - * border switches, then forward packets according to dst MAC address. + * Signals the synchronizer that the SDN-IP leadership has changed. * - * @param prefix IP prefix of the route to add - * @param nextHopIpAddress IP address of the next hop - * @param nextHopMacAddress MAC address of the next hop - * @return the generated intent, or null if no intent should be submitted + * @param isLeader true if this instance is now the leader, otherwise false */ - private MultiPointToSinglePointIntent generateRouteIntent( - IpPrefix prefix, - IpAddress nextHopIpAddress, - MacAddress nextHopMacAddress) { - - // Find the attachment point (egress interface) of the next hop - Interface egressInterface = interfaceService.getMatchingInterface(nextHopIpAddress); - if (egressInterface == null) { - log.warn("No outgoing interface found for {}", - nextHopIpAddress); - return null; - } - - // - // Generate the intent itself - // - Set<ConnectPoint> ingressPorts = new HashSet<>(); - ConnectPoint egressPort = egressInterface.connectPoint(); - log.debug("Generating intent for prefix {}, next hop mac {}", - prefix, nextHopMacAddress); - - for (Interface intf : interfaceService.getInterfaces()) { - // TODO this should be only peering interfaces - if (!intf.connectPoint().equals(egressInterface.connectPoint())) { - ConnectPoint srcPort = intf.connectPoint(); - ingressPorts.add(srcPort); - } - } - - // Match the destination IP prefix at the first hop - TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); - if (prefix.isIp4()) { - selector.matchEthType(Ethernet.TYPE_IPV4); - selector.matchIPDst(prefix); - } else { - selector.matchEthType(Ethernet.TYPE_IPV6); - selector.matchIPv6Dst(prefix); - } - - // Rewrite the destination MAC address - TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder() - .setEthDst(nextHopMacAddress); - if (!egressInterface.vlan().equals(VlanId.NONE)) { - treatment.setVlanId(egressInterface.vlan()); - // If we set VLAN ID, we have to make sure a VLAN tag exists. - // TODO support no VLAN -> VLAN routing - selector.matchVlanId(VlanId.ANY); - } - - int priority = - prefix.prefixLength() * PRIORITY_MULTIPLIER + PRIORITY_OFFSET; - Key key = Key.of(prefix.toString(), appId); - return MultiPointToSinglePointIntent.builder() - .appId(appId) - .key(key) - .selector(selector.build()) - .treatment(treatment.build()) - .ingressPoints(ingressPorts) - .egressPoint(egressPort) - .priority(priority) - .constraints(CONSTRAINTS) - .build(); - } - - @Override - public void setUpConnectivityInternetToHost(IpAddress hostIpAddress) { - checkNotNull(hostIpAddress); - Set<ConnectPoint> ingressPoints = - configService.getBgpPeerConnectPoints(); - - TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); - - if (hostIpAddress.isIp4()) { - selector.matchEthType(Ethernet.TYPE_IPV4); - } else { - selector.matchEthType(Ethernet.TYPE_IPV6); - } + public void leaderChanged(boolean isLeader) { + log.debug("SDN-IP Leader changed: {}", isLeader); - // Match the destination IP prefix at the first hop - IpPrefix ipPrefix = hostIpAddress.toIpPrefix(); - selector.matchIPDst(ipPrefix); - - // Rewrite the destination MAC address - MacAddress hostMac = null; - ConnectPoint egressPoint = null; - for (Host host : hostService.getHostsByIp(hostIpAddress)) { - if (host.mac() != null) { - hostMac = host.mac(); - egressPoint = host.location(); - break; - } - } - if (hostMac == null) { - hostService.startMonitoringIp(hostIpAddress); - return; + if (!isLeader) { + this.isElectedLeader = false; + this.isActivatedLeader = false; + return; // Nothing to do } + this.isActivatedLeader = false; + this.isElectedLeader = true; - TrafficTreatment.Builder treatment = - DefaultTrafficTreatment.builder().setEthDst(hostMac); - Key key = Key.of(ipPrefix.toString(), appId); - int priority = ipPrefix.prefixLength() * PRIORITY_MULTIPLIER - + PRIORITY_OFFSET; - MultiPointToSinglePointIntent intent = - MultiPointToSinglePointIntent.builder() - .appId(appId) - .key(key) - .selector(selector.build()) - .treatment(treatment.build()) - .ingressPoints(ingressPoints) - .egressPoint(egressPoint) - .priority(priority) - .constraints(CONSTRAINTS) - .build(); - - log.trace("Generates ConnectivityInternetToHost intent {}", intent); - submitReactiveIntent(ipPrefix, intent); - } - - - @Override - public void update(Collection<FibUpdate> updates, Collection<FibUpdate> withdraws) { - // - // NOTE: Semantically, we MUST withdraw existing intents before - // submitting new intents. - // - synchronized (this) { - MultiPointToSinglePointIntent intent; - - log.debug("SDN-IP submitting intents = {} withdrawing = {}", - updates.size(), withdraws.size()); - - // - // Prepare the Intent batch operations for the intents to withdraw - // - for (FibUpdate withdraw : withdraws) { - checkArgument(withdraw.type() == FibUpdate.Type.DELETE, - "FibUpdate with wrong type in withdraws list"); - - IpPrefix prefix = withdraw.entry().prefix(); - intent = routeIntents.remove(prefix); - if (intent == null) { - log.trace("SDN-IP No intent in routeIntents to delete " + - "for prefix: {}", prefix); - continue; - } - if (isElectedLeader && isActivatedLeader) { - log.trace("SDN-IP Withdrawing intent: {}", intent); - intentService.withdraw(intent); - } - } - - // - // Prepare the Intent batch operations for the intents to submit - // - for (FibUpdate update : updates) { - checkArgument(update.type() == FibUpdate.Type.UPDATE, - "FibUpdate with wrong type in updates list"); - - IpPrefix prefix = update.entry().prefix(); - intent = generateRouteIntent(prefix, update.entry().nextHopIp(), - update.entry().nextHopMac()); - - if (intent == null) { - // This preserves the old semantics - if an intent can't be - // generated, we don't do anything with that prefix. But - // perhaps we should withdraw the old intent anyway? - continue; - } - - MultiPointToSinglePointIntent oldIntent = - routeIntents.put(prefix, intent); - if (isElectedLeader && isActivatedLeader) { - if (oldIntent != null) { - log.trace("SDN-IP Withdrawing old intent: {}", - oldIntent); - intentService.withdraw(oldIntent); - } - log.trace("SDN-IP Submitting intent: {}", intent); - intentService.submit(intent); - } - } - } + // Run the synchronization method off-thread + bgpIntentsSynchronizerExecutor.execute(this::synchronizeIntents); } - /** - * Synchronize the in-memory Intents with the Intents in the Intent - * framework. - */ - void synchronizeIntents() { - synchronized (this) { - - Map<IntentKey, Intent> localIntents = new HashMap<>(); - Map<IntentKey, Intent> fetchedIntents = new HashMap<>(); - Collection<Intent> storeInMemoryIntents = new LinkedList<>(); - Collection<Intent> addIntents = new LinkedList<>(); - Collection<Intent> deleteIntents = new LinkedList<>(); - - if (!isElectedLeader) { - return; // Nothing to do: not the leader anymore - } - log.debug("SDN-IP synchronizing all intents..."); - - // Prepare the local intents - for (Intent intent : routeIntents.values()) { - localIntents.put(new IntentKey(intent), intent); - } - for (Intent intent : peerIntents.values()) { - localIntents.put(new IntentKey(intent), intent); + private void synchronizeIntents() { + Map<Key, Intent> serviceIntents = new HashMap<>(); + intentService.getIntents().forEach(i -> { + if (i.appId().equals(appId)) { + serviceIntents.put(i.key(), i); } + }); - // Fetch all intents for this application - for (Intent intent : intentService.getIntents()) { - if (!intent.appId().equals(appId)) { - continue; - } - fetchedIntents.put(new IntentKey(intent), intent); - } - if (log.isDebugEnabled()) { - for (Intent intent: fetchedIntents.values()) { - log.trace("SDN-IP Intent Synchronizer: fetched intent: {}", - intent); - } - } - - computeIntentsDelta(localIntents, fetchedIntents, - storeInMemoryIntents, addIntents, - deleteIntents); + List<Intent> intentsToAdd = new LinkedList<>(); + List<Intent> intentsToRemove = new LinkedList<>(); - // - // Perform the actions: - // 1. Store in memory fetched intents that are same. Can be done - // even if we are not the leader anymore - // 2. Delete intents: check if the leader before the operation - // 3. Add intents: check if the leader before the operation - // - for (Intent intent : storeInMemoryIntents) { - // Store the intent in memory based on its type - if (intent instanceof MultiPointToSinglePointIntent) { - MultiPointToSinglePointIntent mp2pIntent = - (MultiPointToSinglePointIntent) intent; - // Find the IP prefix - Criterion c = - mp2pIntent.selector().getCriterion(Criterion.Type.IPV4_DST); - if (c == null) { - // Try IPv6 - c = - mp2pIntent.selector().getCriterion(Criterion.Type.IPV6_DST); - } - if (c != null && c instanceof IPCriterion) { - IPCriterion ipCriterion = (IPCriterion) c; - IpPrefix ipPrefix = ipCriterion.ip(); - if (ipPrefix == null) { - continue; - } - log.trace("SDN-IP Intent Synchronizer: updating " + - "in-memory Route Intent for prefix {}", - ipPrefix); - routeIntents.put(ipPrefix, mp2pIntent); - } else { - log.warn("SDN-IP no IPV4_DST or IPV6_DST criterion found for Intent {}", - mp2pIntent.id()); - } - continue; - } - if (intent instanceof PointToPointIntent) { - PointToPointIntent p2pIntent = (PointToPointIntent) intent; - log.trace("SDN-IP Intent Synchronizer: updating " + - "in-memory Peer Intent {}", p2pIntent); - peerIntents.put(new IntentKey(intent), p2pIntent); - continue; - } - } - - // Withdraw Intents - for (Intent intent : deleteIntents) { - intentService.withdraw(intent); - log.trace("SDN-IP Intent Synchronizer: withdrawing intent: {}", - intent); - } - if (!isElectedLeader) { - log.trace("SDN-IP Intent Synchronizer: cannot withdraw intents: " + - "not elected leader anymore"); - isActivatedLeader = false; - return; - } - - // Add Intents - for (Intent intent : addIntents) { - intentService.submit(intent); - log.trace("SDN-IP Intent Synchronizer: submitting intent: {}", - intent); - } - if (!isElectedLeader) { - log.trace("SDN-IP Intent Synchronizer: cannot submit intents: " + - "not elected leader anymore"); - isActivatedLeader = false; - return; - } - - if (isElectedLeader) { - isActivatedLeader = true; // Allow push of Intents + for (Intent localIntent : intents.values()) { + Intent serviceIntent = serviceIntents.remove(localIntent.key()); + if (serviceIntent == null) { + intentsToAdd.add(localIntent); } else { - isActivatedLeader = false; - } - log.debug("SDN-IP intent synchronization completed"); - } - } - - /** - * Computes the delta in two sets of Intents: local in-memory Intents, - * and intents fetched from the Intent framework. - * - * @param localIntents the local in-memory Intents - * @param fetchedIntents the Intents fetched from the Intent framework - * @param storeInMemoryIntents the Intents that should be stored in memory. - * Note: This Collection must be allocated by the caller, and it will - * be populated by this method. - * @param addIntents the Intents that should be added to the Intent - * framework. Note: This Collection must be allocated by the caller, and - * it will be populated by this method. - * @param deleteIntents the Intents that should be deleted from the Intent - * framework. Note: This Collection must be allocated by the caller, and - * it will be populated by this method. - */ - private void computeIntentsDelta( - final Map<IntentKey, Intent> localIntents, - final Map<IntentKey, Intent> fetchedIntents, - Collection<Intent> storeInMemoryIntents, - Collection<Intent> addIntents, - Collection<Intent> deleteIntents) { - - // - // Compute the deltas between the LOCAL in-memory Intents and the - // FETCHED Intents: - // - If an Intent is in both the LOCAL and FETCHED sets: - // If the FETCHED Intent is WITHDRAWING or WITHDRAWN, then - // the LOCAL Intent should be added/installed; otherwise the - // FETCHED intent should be stored in the local memory - // (i.e., override the LOCAL Intent) to preserve the original - // Intent ID. - // - if a LOCAL Intent is not in the FETCHED set, then the LOCAL - // Intent should be added/installed. - // - If a FETCHED Intent is not in the LOCAL set, then the FETCHED - // Intent should be deleted/withdrawn. - // - for (Map.Entry<IntentKey, Intent> entry : localIntents.entrySet()) { - IntentKey intentKey = entry.getKey(); - Intent localIntent = entry.getValue(); - Intent fetchedIntent = fetchedIntents.get(intentKey); - - if (fetchedIntent == null) { - // - // No FETCHED Intent found: push the LOCAL Intent. - // - addIntents.add(localIntent); - continue; - } - - IntentState state = - intentService.getIntentState(fetchedIntent.key()); - if (state == null || - state == IntentState.WITHDRAWING || - state == IntentState.WITHDRAWN) { - // The intent has been withdrawn but according to our route - // table it should be installed. We'll reinstall it. - addIntents.add(localIntent); - continue; + IntentState state = intentService.getIntentState(serviceIntent.key()); + if (!IntentUtils.equals(serviceIntent, localIntent) || state == null || + state == IntentState.WITHDRAW_REQ || + state == IntentState.WITHDRAWING || + state == IntentState.WITHDRAWN) { + intentsToAdd.add(localIntent); + } } - storeInMemoryIntents.add(fetchedIntent); } - for (Map.Entry<IntentKey, Intent> entry : fetchedIntents.entrySet()) { - IntentKey intentKey = entry.getKey(); - Intent fetchedIntent = entry.getValue(); - Intent localIntent = localIntents.get(intentKey); - - if (localIntent != null) { - continue; + for (Intent serviceIntent : serviceIntents.values()) { + IntentState state = intentService.getIntentState(serviceIntent.key()); + if (state != null && state != IntentState.WITHDRAW_REQ + && state != IntentState.WITHDRAWING + && state != IntentState.WITHDRAWN) { + intentsToRemove.add(serviceIntent); } - - IntentState state = - intentService.getIntentState(fetchedIntent.key()); - if (state == null || - state == IntentState.WITHDRAWING || - state == IntentState.WITHDRAWN) { - // Nothing to do. The intent has been already withdrawn. - continue; - } - // - // No LOCAL Intent found: delete/withdraw the FETCHED Intent. - // - deleteIntents.add(fetchedIntent); - } - } - - /** - * Helper class that can be used to compute the key for an Intent by - * by excluding the Intent ID. - */ - static final class IntentKey { - private final Intent intent; - - /** - * Constructor. - * - * @param intent the intent to use - */ - IntentKey(Intent intent) { - checkArgument((intent instanceof MultiPointToSinglePointIntent) || - (intent instanceof PointToPointIntent), - "Intent type not recognized", intent); - this.intent = intent; } - /** - * Compares two Multi-Point to Single-Point Intents whether they - * represent same logical intention. - * - * @param intent1 the first Intent to compare - * @param intent2 the second Intent to compare - * @return true if both Intents represent same logical intention, - * otherwise false - */ - static boolean equalIntents(MultiPointToSinglePointIntent intent1, - MultiPointToSinglePointIntent intent2) { - return Objects.equals(intent1.appId(), intent2.appId()) && - Objects.equals(intent1.selector(), intent2.selector()) && - Objects.equals(intent1.treatment(), intent2.treatment()) && - Objects.equals(intent1.ingressPoints(), intent2.ingressPoints()) && - Objects.equals(intent1.egressPoint(), intent2.egressPoint()); - } + log.debug("SDN-IP Intent Synchronizer: submitting {}, withdrawing {}", + intentsToAdd.size(), intentsToRemove.size()); - /** - * Compares two Point-to-Point Intents whether they represent - * same logical intention. - * - * @param intent1 the first Intent to compare - * @param intent2 the second Intent to compare - * @return true if both Intents represent same logical intention, - * otherwise false - */ - static boolean equalIntents(PointToPointIntent intent1, - PointToPointIntent intent2) { - return Objects.equals(intent1.appId(), intent2.appId()) && - Objects.equals(intent1.selector(), intent2.selector()) && - Objects.equals(intent1.treatment(), intent2.treatment()) && - Objects.equals(intent1.ingressPoint(), intent2.ingressPoint()) && - Objects.equals(intent1.egressPoint(), intent2.egressPoint()); + // Withdraw Intents + for (Intent intent : intentsToRemove) { + intentService.withdraw(intent); + log.trace("SDN-IP Intent Synchronizer: withdrawing intent: {}", + intent); } - - @Override - public int hashCode() { - if (intent instanceof PointToPointIntent) { - PointToPointIntent p2pIntent = (PointToPointIntent) intent; - return Objects.hash(p2pIntent.appId(), - p2pIntent.resources(), - p2pIntent.selector(), - p2pIntent.treatment(), - p2pIntent.constraints(), - p2pIntent.ingressPoint(), - p2pIntent.egressPoint()); - } - if (intent instanceof MultiPointToSinglePointIntent) { - MultiPointToSinglePointIntent m2pIntent = - (MultiPointToSinglePointIntent) intent; - return Objects.hash(m2pIntent.appId(), - m2pIntent.resources(), - m2pIntent.selector(), - m2pIntent.treatment(), - m2pIntent.constraints(), - m2pIntent.ingressPoints(), - m2pIntent.egressPoint()); - } - checkArgument(false, "Intent type not recognized", intent); - return 0; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if ((obj == null) || (!(obj instanceof IntentKey))) { - return false; - } - IntentKey other = (IntentKey) obj; - - if (this.intent instanceof PointToPointIntent) { - if (!(other.intent instanceof PointToPointIntent)) { - return false; - } - return equalIntents((PointToPointIntent) this.intent, - (PointToPointIntent) other.intent); - } - if (this.intent instanceof MultiPointToSinglePointIntent) { - if (!(other.intent instanceof MultiPointToSinglePointIntent)) { - return false; - } - return equalIntents( - (MultiPointToSinglePointIntent) this.intent, - (MultiPointToSinglePointIntent) other.intent); - } - checkArgument(false, "Intent type not recognized", intent); - return false; + if (!isElectedLeader) { + log.debug("SDN-IP Intent Synchronizer: cannot withdraw intents: " + + "not elected leader anymore"); + isActivatedLeader = false; + return; } - } - @Override - public void setUpConnectivityHostToHost(IpAddress dstIpAddress, - IpAddress srcIpAddress, - MacAddress srcMacAddress, - ConnectPoint srcConnectPoint) { - checkNotNull(dstIpAddress); - checkNotNull(srcIpAddress); - checkNotNull(srcMacAddress); - checkNotNull(srcConnectPoint); - - IpPrefix srcIpPrefix = srcIpAddress.toIpPrefix(); - IpPrefix dstIpPrefix = dstIpAddress.toIpPrefix(); - ConnectPoint dstConnectPoint = null; - MacAddress dstMacAddress = null; - - for (Host host : hostService.getHostsByIp(dstIpAddress)) { - if (host.mac() != null) { - dstMacAddress = host.mac(); - dstConnectPoint = host.location(); - break; - } + // Add Intents + for (Intent intent : intentsToAdd) { + intentService.submit(intent); + log.trace("SDN-IP Intent Synchronizer: submitting intent: {}", + intent); } - if (dstMacAddress == null) { - hostService.startMonitoringIp(dstIpAddress); + if (!isElectedLeader) { + log.debug("SDN-IP Intent Synchronizer: cannot submit intents: " + + "not elected leader anymore"); + isActivatedLeader = false; return; } - // - // Handle intent from source host to destination host - // - MultiPointToSinglePointIntent srcToDstIntent = - hostToHostIntentGenerator(dstIpAddress, dstConnectPoint, - dstMacAddress, srcConnectPoint); - submitReactiveIntent(dstIpPrefix, srcToDstIntent); - - // - // Handle intent from destination host to source host - // - - // Since we proactively handle the intent from destination host to - // source host, we should check whether there is an exiting intent - // first. - if (mp2pIntentExists(srcIpPrefix)) { - updateExistingMp2pIntent(srcIpPrefix, dstConnectPoint); - return; + if (isElectedLeader) { + isActivatedLeader = true; // Allow push of Intents } else { - // There is no existing intent, create a new one. - MultiPointToSinglePointIntent dstToSrcIntent = - hostToHostIntentGenerator(srcIpAddress, srcConnectPoint, - srcMacAddress, dstConnectPoint); - submitReactiveIntent(srcIpPrefix, dstToSrcIntent); + isActivatedLeader = false; } + log.debug("SDN-IP intent synchronization completed"); } - /** - * Generates MultiPointToSinglePointIntent for both source host and - * destination host located in local SDN network. - * - * @param dstIpAddress the destination IP address - * @param dstConnectPoint the destination host connect point - * @param dstMacAddress the MAC address of destination host - * @param srcConnectPoint the connect point where packet-in from - * @return the generated MultiPointToSinglePointIntent - */ - private MultiPointToSinglePointIntent hostToHostIntentGenerator( - IpAddress dstIpAddress, - ConnectPoint dstConnectPoint, - MacAddress dstMacAddress, - ConnectPoint srcConnectPoint) { - checkNotNull(dstIpAddress); - checkNotNull(dstConnectPoint); - checkNotNull(dstMacAddress); - checkNotNull(srcConnectPoint); - - Set<ConnectPoint> ingressPoints = new HashSet<>(); - ingressPoints.add(srcConnectPoint); - IpPrefix dstIpPrefix = dstIpAddress.toIpPrefix(); - - TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); - if (dstIpAddress.isIp4()) { - selector.matchEthType(Ethernet.TYPE_IPV4); - selector.matchIPDst(dstIpPrefix); - } else { - selector.matchEthType(Ethernet.TYPE_IPV6); - selector.matchIPv6Dst(dstIpPrefix); - } - - // Rewrite the destination MAC address - TrafficTreatment.Builder treatment = - DefaultTrafficTreatment.builder().setEthDst(dstMacAddress); - - Key key = Key.of(dstIpPrefix.toString(), appId); - int priority = dstIpPrefix.prefixLength() * PRIORITY_MULTIPLIER - + PRIORITY_OFFSET; - MultiPointToSinglePointIntent intent = - MultiPointToSinglePointIntent.builder() - .appId(appId) - .key(key) - .selector(selector.build()) - .treatment(treatment.build()) - .ingressPoints(ingressPoints) - .egressPoint(dstConnectPoint) - .priority(priority) - .constraints(CONSTRAINTS) - .build(); - - log.trace("Generates ConnectivityHostToHost = {} ", intent); - return intent; - } - - @Override - public void updateExistingMp2pIntent(IpPrefix ipPrefix, - ConnectPoint ingressConnectPoint) { - checkNotNull(ipPrefix); - checkNotNull(ingressConnectPoint); - - MultiPointToSinglePointIntent existingIntent = - getExistingMp2pIntent(ipPrefix); - if (existingIntent != null) { - Set<ConnectPoint> ingressPoints = existingIntent.ingressPoints(); - // Add host connect point into ingressPoints of the existing intent - if (ingressPoints.add(ingressConnectPoint)) { - MultiPointToSinglePointIntent updatedMp2pIntent = - MultiPointToSinglePointIntent.builder() - .appId(appId) - .key(existingIntent.key()) - .selector(existingIntent.selector()) - .treatment(existingIntent.treatment()) - .ingressPoints(ingressPoints) - .egressPoint(existingIntent.egressPoint()) - .priority(existingIntent.priority()) - .constraints(CONSTRAINTS) - .build(); - - log.trace("Update an existing MultiPointToSinglePointIntent " - + "to new intent = {} ", updatedMp2pIntent); - submitReactiveIntent(ipPrefix, updatedMp2pIntent); - } - // If adding ingressConnectPoint to ingressPoints failed, it - // because between the time interval from checking existing intent - // to generating new intent, onos updated this intent due to other - // packet-in and the new intent also includes the - // ingressConnectPoint. This will not affect reactive routing. - } - } - - @Override - public boolean mp2pIntentExists(IpPrefix ipPrefix) { - checkNotNull(ipPrefix); - return routeIntents.get(ipPrefix) != null; - } - - /** - * Gets the existing MultiPointToSinglePointIntent from memory for a given - * IP prefix. - * - * @param ipPrefix the IP prefix used to find MultiPointToSinglePointIntent - * @return the MultiPointToSinglePointIntent if found, otherwise null - */ - private MultiPointToSinglePointIntent getExistingMp2pIntent(IpPrefix - ipPrefix) { - checkNotNull(ipPrefix); - return routeIntents.get(ipPrefix); - } } |