aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/onos/apps/sdnip/src
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/onos/apps/sdnip/src')
-rw-r--r--framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java901
-rw-r--r--framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentUtils.java81
-rw-r--r--framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/PeerConnectivityManager.java74
-rw-r--r--framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java18
-rw-r--r--framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpFib.java216
-rw-r--r--framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/PrimaryChangeCommand.java2
-rw-r--r--framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java460
-rw-r--r--framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/PeerConnectivityManagerTest.java81
-rw-r--r--framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/SdnIpFibTest.java417
-rw-r--r--framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/TestIntentServiceHelper.java7
10 files changed, 1028 insertions, 1229 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);
- }
}
diff --git a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentUtils.java b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentUtils.java
new file mode 100644
index 00000000..8e2a3df3
--- /dev/null
+++ b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentUtils.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.sdnip;
+
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.MultiPointToSinglePointIntent;
+import org.onosproject.net.intent.PointToPointIntent;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Utilities for dealing with intents.
+ */
+public final class IntentUtils {
+
+ private static final Logger log = LoggerFactory.getLogger(IntentUtils.class);
+
+ private IntentUtils() {
+
+ }
+
+ /**
+ * Checks if two intents represent the same value.
+ *
+ * <p>({@link Intent#equals(Object)} only checks ID equality)</p>
+ *
+ * <p>Both intents must be of the same type.</p>
+ *
+ * @param one first intent
+ * @param two second intent
+ * @return true if the two intents represent the same value, otherwise false
+ */
+ public static boolean equals(Intent one, Intent two) {
+ checkArgument(one.getClass() == two.getClass(),
+ "Intents are not the same type");
+
+ if (!(Objects.equals(one.appId(), two.appId()) &&
+ Objects.equals(one.key(), two.key()))) {
+ return false;
+ }
+
+ if (one instanceof MultiPointToSinglePointIntent) {
+ MultiPointToSinglePointIntent intent1 = (MultiPointToSinglePointIntent) one;
+ MultiPointToSinglePointIntent intent2 = (MultiPointToSinglePointIntent) two;
+
+ return Objects.equals(intent1.selector(), intent2.selector()) &&
+ Objects.equals(intent1.treatment(), intent2.treatment()) &&
+ Objects.equals(intent1.ingressPoints(), intent2.ingressPoints()) &&
+ Objects.equals(intent1.egressPoint(), intent2.egressPoint());
+ } else if (one instanceof PointToPointIntent) {
+ PointToPointIntent intent1 = (PointToPointIntent) one;
+ PointToPointIntent intent2 = (PointToPointIntent) two;
+
+ return Objects.equals(intent1.selector(), intent2.selector()) &&
+ Objects.equals(intent1.treatment(), intent2.treatment()) &&
+ Objects.equals(intent1.ingressPoint(), intent2.ingressPoint()) &&
+ Objects.equals(intent1.egressPoint(), intent2.egressPoint());
+ } else {
+ log.error("Unimplemented intent type");
+ return false;
+ }
+ }
+}
diff --git a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/PeerConnectivityManager.java b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/PeerConnectivityManager.java
index 459db2b7..b2ce0f8a 100644
--- a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/PeerConnectivityManager.java
+++ b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/PeerConnectivityManager.java
@@ -15,6 +15,8 @@
*/
package org.onosproject.sdnip;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
import org.onlab.packet.Ethernet;
import org.onlab.packet.IPv4;
import org.onlab.packet.IPv6;
@@ -22,16 +24,18 @@ import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.TpPort;
import org.onosproject.core.ApplicationId;
-import org.onosproject.net.config.NetworkConfigService;
import org.onosproject.incubator.net.intf.Interface;
import org.onosproject.incubator.net.intf.InterfaceService;
import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.config.NetworkConfigService;
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.host.InterfaceIpAddress;
+import org.onosproject.net.intent.Key;
import org.onosproject.net.intent.PointToPointIntent;
+import org.onosproject.routing.IntentSynchronizationService;
import org.onosproject.routing.RoutingService;
import org.onosproject.routing.config.BgpConfig;
import org.slf4j.Logger;
@@ -49,18 +53,26 @@ import static com.google.common.base.Preconditions.checkNotNull;
public class PeerConnectivityManager {
private static final int PRIORITY_OFFSET = 1000;
+ private static final String SUFFIX_DST = "dst";
+ private static final String SUFFIX_SRC = "src";
+ private static final String SUFFIX_ICMP = "icmp";
+
private static final Logger log = LoggerFactory.getLogger(
PeerConnectivityManager.class);
private static final short BGP_PORT = 179;
- private final IntentSynchronizer intentSynchronizer;
+ private final IntentSynchronizationService intentSynchronizer;
private final NetworkConfigService configService;
private final InterfaceService interfaceService;
private final ApplicationId appId;
private final ApplicationId routerAppId;
+ // Just putting something random here for now. Figure out exactly what
+ // indexes we need when we start making use of them.
+ private final Multimap<BgpConfig.BgpSpeakerConfig, PointToPointIntent> peerIntents;
+
/**
* Creates a new PeerConnectivityManager.
*
@@ -71,7 +83,7 @@ public class PeerConnectivityManager {
* @param routerAppId application ID
*/
public PeerConnectivityManager(ApplicationId appId,
- IntentSynchronizer intentSynchronizer,
+ IntentSynchronizationService intentSynchronizer,
NetworkConfigService configService,
ApplicationId routerAppId,
InterfaceService interfaceService) {
@@ -80,6 +92,8 @@ public class PeerConnectivityManager {
this.configService = configService;
this.routerAppId = routerAppId;
this.interfaceService = interfaceService;
+
+ peerIntents = HashMultimap.create();
}
/**
@@ -100,8 +114,6 @@ public class PeerConnectivityManager {
* BGP speakers and external BGP peers.
*/
private void setUpConnectivity() {
- List<PointToPointIntent> intents = new ArrayList<>();
-
BgpConfig config = configService.getConfig(routerAppId, RoutingService.CONFIG_CLASS);
if (config == null) {
@@ -113,11 +125,12 @@ public class PeerConnectivityManager {
log.debug("Start to set up BGP paths for BGP speaker: {}",
bgpSpeaker);
- intents.addAll(buildSpeakerIntents(bgpSpeaker));
- }
+ buildSpeakerIntents(bgpSpeaker).forEach(i -> {
+ peerIntents.put(bgpSpeaker, i);
+ intentSynchronizer.submit(i);
+ });
- // Submit all the intents.
- intentSynchronizer.submitPeerIntents(intents);
+ }
}
private Collection<PointToPointIntent> buildSpeakerIntents(BgpConfig.BgpSpeakerConfig speaker) {
@@ -167,8 +180,8 @@ public class PeerConnectivityManager {
List<PointToPointIntent> intents = new ArrayList<>();
TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
-
TrafficSelector selector;
+ Key key;
byte tcpProtocol;
byte icmpProtocol;
@@ -188,8 +201,11 @@ public class PeerConnectivityManager {
null,
BGP_PORT);
+ key = buildKey(ipOne, ipTwo, SUFFIX_DST);
+
intents.add(PointToPointIntent.builder()
.appId(appId)
+ .key(key)
.selector(selector)
.treatment(treatment)
.ingressPoint(portOne)
@@ -204,8 +220,11 @@ public class PeerConnectivityManager {
BGP_PORT,
null);
+ key = buildKey(ipOne, ipTwo, SUFFIX_SRC);
+
intents.add(PointToPointIntent.builder()
.appId(appId)
+ .key(key)
.selector(selector)
.treatment(treatment)
.ingressPoint(portOne)
@@ -220,8 +239,11 @@ public class PeerConnectivityManager {
null,
BGP_PORT);
+ key = buildKey(ipTwo, ipOne, SUFFIX_DST);
+
intents.add(PointToPointIntent.builder()
.appId(appId)
+ .key(key)
.selector(selector)
.treatment(treatment)
.ingressPoint(portTwo)
@@ -236,8 +258,11 @@ public class PeerConnectivityManager {
BGP_PORT,
null);
+ key = buildKey(ipTwo, ipOne, SUFFIX_SRC);
+
intents.add(PointToPointIntent.builder()
.appId(appId)
+ .key(key)
.selector(selector)
.treatment(treatment)
.ingressPoint(portTwo)
@@ -252,8 +277,11 @@ public class PeerConnectivityManager {
null,
null);
+ key = buildKey(ipOne, ipTwo, SUFFIX_ICMP);
+
intents.add(PointToPointIntent.builder()
.appId(appId)
+ .key(key)
.selector(selector)
.treatment(treatment)
.ingressPoint(portOne)
@@ -268,8 +296,11 @@ public class PeerConnectivityManager {
null,
null);
+ key = buildKey(ipTwo, ipOne, SUFFIX_ICMP);
+
intents.add(PointToPointIntent.builder()
.appId(appId)
+ .key(key)
.selector(selector)
.treatment(treatment)
.ingressPoint(portTwo)
@@ -316,4 +347,27 @@ public class PeerConnectivityManager {
return builder.build();
}
+ /**
+ * Builds an intent Key for a point-to-point intent based off the source
+ * and destination IP address, as well as a suffix String to distinguish
+ * between different types of intents between the same source and
+ * destination.
+ *
+ * @param srcIp source IP address
+ * @param dstIp destination IP address
+ * @param suffix suffix string
+ * @return
+ */
+ private Key buildKey(IpAddress srcIp, IpAddress dstIp, String suffix) {
+ String keyString = new StringBuilder()
+ .append(srcIp.toString())
+ .append("-")
+ .append(dstIp.toString())
+ .append("-")
+ .append(suffix)
+ .toString();
+
+ return Key.of(keyString, appId);
+ }
+
}
diff --git a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java
index 3d1fe65c..1b3eda9d 100644
--- a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java
+++ b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java
@@ -32,7 +32,9 @@ import org.onosproject.net.config.NetworkConfigService;
import org.onosproject.incubator.net.intf.InterfaceService;
import org.onosproject.net.host.HostService;
import org.onosproject.net.intent.IntentService;
+import org.onosproject.routing.IntentSynchronizationService;
import org.onosproject.routing.RoutingService;
+import org.onosproject.routing.SdnIpService;
import org.onosproject.routing.config.RoutingConfigurationService;
import org.slf4j.Logger;
@@ -79,6 +81,7 @@ public class SdnIp implements SdnIpService {
private IntentSynchronizer intentSynchronizer;
private PeerConnectivityManager peerConnectivity;
+ private SdnIpFib fib;
private LeadershipEventListener leadershipEventListener =
new InnerLeadershipEventListener();
@@ -93,10 +96,7 @@ public class SdnIp implements SdnIpService {
localControllerNode = clusterService.getLocalNode();
- intentSynchronizer = new IntentSynchronizer(appId, intentService,
- hostService,
- config,
- interfaceService);
+ intentSynchronizer = new IntentSynchronizer(appId, intentService);
intentSynchronizer.start();
peerConnectivity = new PeerConnectivityManager(appId,
@@ -106,8 +106,9 @@ public class SdnIp implements SdnIpService {
interfaceService);
peerConnectivity.start();
- routingService.addFibListener(intentSynchronizer);
- routingService.addIntentRequestListener(intentSynchronizer);
+ fib = new SdnIpFib(appId, interfaceService, intentSynchronizer);
+
+ routingService.addFibListener(fib);
routingService.start();
leadershipService.addListener(leadershipEventListener);
@@ -131,6 +132,11 @@ public class SdnIp implements SdnIpService {
intentSynchronizer.leaderChanged(isPrimary);
}
+ @Override
+ public IntentSynchronizationService getIntentSynchronizationService() {
+ return intentSynchronizer;
+ }
+
/**
* Converts DPIDs of the form xx:xx:xx:xx:xx:xx:xx to OpenFlow provider
* device URIs.
diff --git a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpFib.java b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpFib.java
new file mode 100644
index 00000000..c0001bdc
--- /dev/null
+++ b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpFib.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.sdnip;
+
+import com.google.common.collect.ImmutableList;
+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.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.intent.Constraint;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.MultiPointToSinglePointIntent;
+import org.onosproject.net.intent.constraint.PartialFailureConstraint;
+import org.onosproject.routing.FibListener;
+import org.onosproject.routing.FibUpdate;
+import org.onosproject.routing.IntentSynchronizationService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * FIB component of SDN-IP.
+ */
+public class SdnIpFib implements FibListener {
+ private Logger log = LoggerFactory.getLogger(getClass());
+
+ private static final int PRIORITY_OFFSET = 100;
+ private static final int PRIORITY_MULTIPLIER = 5;
+ protected static final ImmutableList<Constraint> CONSTRAINTS
+ = ImmutableList.of(new PartialFailureConstraint());
+
+ private final Map<IpPrefix, MultiPointToSinglePointIntent> routeIntents;
+
+ private final ApplicationId appId;
+ private final InterfaceService interfaceService;
+ private final IntentSynchronizationService intentSynchronizer;
+
+ /**
+ * Class constructor.
+ *
+ * @param appId application ID to use when generating intents
+ * @param interfaceService interface service
+ * @param intentSynchronizer intent synchronizer
+ */
+ public SdnIpFib(ApplicationId appId, InterfaceService interfaceService,
+ IntentSynchronizationService intentSynchronizer) {
+ routeIntents = new ConcurrentHashMap<>();
+
+ this.appId = appId;
+ this.interfaceService = interfaceService;
+ this.intentSynchronizer = intentSynchronizer;
+ }
+
+
+ @Override
+ public void update(Collection<FibUpdate> updates, Collection<FibUpdate> withdraws) {
+ int submitCount = 0, withdrawCount = 0;
+ //
+ // NOTE: Semantically, we MUST withdraw existing intents before
+ // submitting new intents.
+ //
+ synchronized (this) {
+ MultiPointToSinglePointIntent intent;
+
+ //
+ // 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;
+ }
+ intentSynchronizer.withdraw(intent);
+ withdrawCount++;
+ }
+
+ //
+ // 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;
+ }
+
+ routeIntents.put(prefix, intent);
+ intentSynchronizer.submit(intent);
+ submitCount++;
+ }
+
+ log.debug("SDN-IP submitted {}/{}, withdrew = {}/{}", submitCount,
+ updates.size(), withdrawCount, withdraws.size());
+ }
+ }
+
+ /**
+ * 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.
+ *
+ * @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
+ */
+ 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();
+ }
+
+}
diff --git a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/PrimaryChangeCommand.java b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/PrimaryChangeCommand.java
index 72cc112e..7a17cfe0 100644
--- a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/PrimaryChangeCommand.java
+++ b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/PrimaryChangeCommand.java
@@ -18,7 +18,7 @@ package org.onosproject.sdnip.cli;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.sdnip.SdnIpService;
+import org.onosproject.routing.SdnIpService;
/**
* Command to change whether this SDNIP instance is primary or not.
diff --git a/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java b/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java
index fc5782e4..6dc3ce10 100644
--- a/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java
+++ b/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java
@@ -16,6 +16,7 @@
package org.onosproject.sdnip;
import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.MoreExecutors;
import org.junit.Before;
import org.junit.Test;
import org.onlab.junit.TestUtils;
@@ -27,10 +28,9 @@ import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
+import org.onosproject.TestApplicationId;
import org.onosproject.core.ApplicationId;
-import org.onosproject.net.config.NetworkConfigService;
import org.onosproject.incubator.net.intf.Interface;
-import org.onosproject.incubator.net.intf.InterfaceService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
@@ -43,20 +43,13 @@ import org.onosproject.net.intent.AbstractIntentTest;
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.routing.FibEntry;
-import org.onosproject.routing.FibUpdate;
import org.onosproject.routing.RouteEntry;
-import org.onosproject.routing.config.BgpPeer;
-import org.onosproject.routing.config.RoutingConfigurationService;
-import org.onosproject.sdnip.IntentSynchronizer.IntentKey;
import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
-import java.util.Map;
import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
@@ -64,11 +57,8 @@ import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.reset;
import static org.easymock.EasyMock.verify;
import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.onosproject.sdnip.TestIntentServiceHelper.eqExceptId;
/**
* This class tests the intent synchronization function in the
@@ -76,10 +66,7 @@ import static org.onosproject.sdnip.TestIntentServiceHelper.eqExceptId;
*/
public class IntentSyncTest extends AbstractIntentTest {
- private RoutingConfigurationService routingConfig;
- private InterfaceService interfaceService;
private IntentService intentService;
- private NetworkConfigService configService;
private static final ConnectPoint SW1_ETH1 = new ConnectPoint(
DeviceId.deviceId("of:0000000000000001"),
@@ -100,65 +87,18 @@ public class IntentSyncTest extends AbstractIntentTest {
private IntentSynchronizer intentSynchronizer;
private final Set<Interface> interfaces = Sets.newHashSet();
- private static final ApplicationId APPID = new ApplicationId() {
- @Override
- public short id() {
- return 1;
- }
-
- @Override
- public String name() {
- return "SDNIP";
- }
- };
+ private static final ApplicationId APPID = TestApplicationId.create("SDNIP");
@Before
public void setUp() throws Exception {
super.setUp();
- routingConfig = createMock(RoutingConfigurationService.class);
- interfaceService = createMock(InterfaceService.class);
- configService = createMock(NetworkConfigService.class);
-
- // These will set expectations on routingConfig
setUpInterfaceService();
- setUpBgpPeers();
-
- replay(routingConfig);
- replay(interfaceService);
intentService = createMock(IntentService.class);
intentSynchronizer = new IntentSynchronizer(APPID, intentService,
- null, routingConfig,
- interfaceService);
- }
-
- /**
- * Sets up BGP peers in external networks.
- */
- private void setUpBgpPeers() {
-
- Map<IpAddress, BgpPeer> peers = new HashMap<>();
-
- String peerSw1Eth1 = "192.168.10.1";
- peers.put(IpAddress.valueOf(peerSw1Eth1),
- new BgpPeer("00:00:00:00:00:00:00:01", 1, peerSw1Eth1));
-
- // Two BGP peers are connected to switch 2 port 1.
- String peer1Sw2Eth1 = "192.168.20.1";
- peers.put(IpAddress.valueOf(peer1Sw2Eth1),
- new BgpPeer("00:00:00:00:00:00:00:02", 1, peer1Sw2Eth1));
-
- String peer2Sw2Eth1 = "192.168.20.2";
- peers.put(IpAddress.valueOf(peer2Sw2Eth1),
- new BgpPeer("00:00:00:00:00:00:00:02", 1, peer2Sw2Eth1));
-
- String peer1Sw4Eth1 = "192.168.40.1";
- peers.put(IpAddress.valueOf(peer1Sw4Eth1),
- new BgpPeer("00:00:00:00:00:00:00:04", 1, peer1Sw4Eth1));
-
- expect(routingConfig.getBgpPeers()).andReturn(peers).anyTimes();
+ MoreExecutors.newDirectExecutorService());
}
/**
@@ -200,267 +140,13 @@ public class IntentSyncTest extends AbstractIntentTest {
MacAddress.valueOf("00:00:00:00:00:04"),
VlanId.vlanId((short) 1));
- expect(interfaceService.getInterfacesByPort(SW4_ETH1)).andReturn(
- Collections.singleton(sw4Eth1)).anyTimes();
- expect(interfaceService.getMatchingInterface(Ip4Address.valueOf("192.168.40.1")))
- .andReturn(sw4Eth1).anyTimes();
-
interfaces.add(sw4Eth1);
-
- expect(interfaceService.getInterfacesByPort(SW1_ETH1)).andReturn(
- Collections.singleton(sw1Eth1)).anyTimes();
- expect(interfaceService.getMatchingInterface(Ip4Address.valueOf("192.168.10.1")))
- .andReturn(sw1Eth1).anyTimes();
- expect(interfaceService.getInterfacesByPort(SW2_ETH1)).andReturn(
- Collections.singleton(sw2Eth1)).anyTimes();
- expect(interfaceService.getMatchingInterface(Ip4Address.valueOf("192.168.20.1")))
- .andReturn(sw2Eth1).anyTimes();
- expect(interfaceService.getInterfacesByPort(SW3_ETH1)).andReturn(
- Collections.singleton(sw3Eth1)).anyTimes();
- expect(interfaceService.getMatchingInterface(Ip4Address.valueOf("192.168.30.1")))
- .andReturn(sw3Eth1).anyTimes();
- expect(interfaceService.getInterfaces()).andReturn(interfaces).anyTimes();
- }
-
- /**
- * Tests adding a FIB entry to the IntentSynchronizer.
- *
- * We verify that the synchronizer records the correct state and that the
- * correct intent is submitted to the IntentService.
- *
- * @throws TestUtilsException
- */
- @Test
- public void testFibAdd() throws TestUtilsException {
- FibEntry fibEntry = new FibEntry(
- Ip4Prefix.valueOf("1.1.1.0/24"),
- Ip4Address.valueOf("192.168.10.1"),
- MacAddress.valueOf("00:00:00:00:00:01"));
-
- // Construct a MultiPointToSinglePointIntent intent
- TrafficSelector.Builder selectorBuilder =
- DefaultTrafficSelector.builder();
- selectorBuilder.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(
- fibEntry.prefix());
-
- TrafficTreatment.Builder treatmentBuilder =
- DefaultTrafficTreatment.builder();
- treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:01"));
-
- Set<ConnectPoint> ingressPoints = new HashSet<>();
- ingressPoints.add(SW2_ETH1);
- ingressPoints.add(SW3_ETH1);
- ingressPoints.add(SW4_ETH1);
-
- MultiPointToSinglePointIntent intent =
- MultiPointToSinglePointIntent.builder()
- .appId(APPID)
- .selector(selectorBuilder.build())
- .treatment(treatmentBuilder.build())
- .ingressPoints(ingressPoints)
- .egressPoint(SW1_ETH1)
- .constraints(IntentSynchronizer.CONSTRAINTS)
- .build();
-
- // Setup the expected intents
- intentService.submit(eqExceptId(intent));
- replay(intentService);
-
- intentSynchronizer.leaderChanged(true);
- TestUtils.setField(intentSynchronizer, "isActivatedLeader", true);
-
- FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.UPDATE,
- fibEntry);
- intentSynchronizer.update(Collections.singleton(fibUpdate),
- Collections.emptyList());
-
- assertEquals(intentSynchronizer.getRouteIntents().size(), 1);
- Intent firstIntent =
- intentSynchronizer.getRouteIntents().iterator().next();
- IntentKey firstIntentKey = new IntentKey(firstIntent);
- IntentKey intentKey = new IntentKey(intent);
- assertTrue(firstIntentKey.equals(intentKey));
- verify(intentService);
- }
-
- /**
- * Tests adding a FIB entry with to a next hop in a VLAN.
- *
- * We verify that the synchronizer records the correct state and that the
- * correct intent is submitted to the IntentService.
- *
- * @throws TestUtilsException
- */
- @Test
- public void testFibAddWithVlan() throws TestUtilsException {
- FibEntry fibEntry = new FibEntry(
- Ip4Prefix.valueOf("3.3.3.0/24"),
- Ip4Address.valueOf("192.168.40.1"),
- MacAddress.valueOf("00:00:00:00:00:04"));
-
- // Construct a MultiPointToSinglePointIntent intent
- TrafficSelector.Builder selectorBuilder =
- DefaultTrafficSelector.builder();
- selectorBuilder.matchEthType(Ethernet.TYPE_IPV4)
- .matchIPDst(fibEntry.prefix())
- .matchVlanId(VlanId.ANY);
-
- TrafficTreatment.Builder treatmentBuilder =
- DefaultTrafficTreatment.builder();
- treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:04"))
- .setVlanId(VlanId.vlanId((short) 1));
-
- Set<ConnectPoint> ingressPoints = new HashSet<>();
- ingressPoints.add(SW1_ETH1);
- ingressPoints.add(SW2_ETH1);
- ingressPoints.add(SW3_ETH1);
-
- MultiPointToSinglePointIntent intent =
- MultiPointToSinglePointIntent.builder()
- .appId(APPID)
- .selector(selectorBuilder.build())
- .treatment(treatmentBuilder.build())
- .ingressPoints(ingressPoints)
- .egressPoint(SW4_ETH1)
- .constraints(IntentSynchronizer.CONSTRAINTS)
- .build();
-
- // Setup the expected intents
- intentService.submit(eqExceptId(intent));
-
- replay(intentService);
-
- // Run the test
- intentSynchronizer.leaderChanged(true);
- TestUtils.setField(intentSynchronizer, "isActivatedLeader", true);
- FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.UPDATE, fibEntry);
-
- intentSynchronizer.update(Collections.singleton(fibUpdate),
- Collections.emptyList());
-
- // Verify
- assertEquals(intentSynchronizer.getRouteIntents().size(), 1);
- Intent firstIntent =
- intentSynchronizer.getRouteIntents().iterator().next();
- IntentKey firstIntentKey = new IntentKey(firstIntent);
- IntentKey intentKey = new IntentKey(intent);
- assertTrue(firstIntentKey.equals(intentKey));
- verify(intentService);
- }
-
- /**
- * Tests updating a FIB entry.
- *
- * We verify that the synchronizer records the correct state and that the
- * correct intent is submitted to the IntentService.
- *
- * @throws TestUtilsException
- */
- @Test
- public void testFibUpdate() throws TestUtilsException {
- // Firstly add a route
- testFibAdd();
-
- Intent addedIntent =
- intentSynchronizer.getRouteIntents().iterator().next();
-
- // Start to construct a new route entry and new intent
- FibEntry fibEntryUpdate = new FibEntry(
- Ip4Prefix.valueOf("1.1.1.0/24"),
- Ip4Address.valueOf("192.168.20.1"),
- MacAddress.valueOf("00:00:00:00:00:02"));
-
- // Construct a new MultiPointToSinglePointIntent intent
- TrafficSelector.Builder selectorBuilderNew =
- DefaultTrafficSelector.builder();
- selectorBuilderNew.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(
- fibEntryUpdate.prefix());
-
- TrafficTreatment.Builder treatmentBuilderNew =
- DefaultTrafficTreatment.builder();
- treatmentBuilderNew.setEthDst(MacAddress.valueOf("00:00:00:00:00:02"));
-
-
- Set<ConnectPoint> ingressPointsNew = new HashSet<>();
- ingressPointsNew.add(SW1_ETH1);
- ingressPointsNew.add(SW3_ETH1);
- ingressPointsNew.add(SW4_ETH1);
-
- MultiPointToSinglePointIntent intentNew =
- MultiPointToSinglePointIntent.builder()
- .appId(APPID)
- .selector(selectorBuilderNew.build())
- .treatment(treatmentBuilderNew.build())
- .ingressPoints(ingressPointsNew)
- .egressPoint(SW2_ETH1)
- .constraints(IntentSynchronizer.CONSTRAINTS)
- .build();
-
- // Set up test expectation
- reset(intentService);
- // Setup the expected intents
- intentService.withdraw(eqExceptId(addedIntent));
- intentService.submit(eqExceptId(intentNew));
- replay(intentService);
-
- // Call the update() method in IntentSynchronizer class
- intentSynchronizer.leaderChanged(true);
- TestUtils.setField(intentSynchronizer, "isActivatedLeader", true);
- FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.UPDATE,
- fibEntryUpdate);
- intentSynchronizer.update(Collections.singletonList(fibUpdate),
- Collections.emptyList());
-
- // Verify
- assertEquals(intentSynchronizer.getRouteIntents().size(), 1);
- Intent firstIntent =
- intentSynchronizer.getRouteIntents().iterator().next();
- IntentKey firstIntentKey = new IntentKey(firstIntent);
- IntentKey intentNewKey = new IntentKey(intentNew);
- assertTrue(firstIntentKey.equals(intentNewKey));
- verify(intentService);
}
/**
- * Tests deleting a FIB entry.
- *
- * We verify that the synchronizer records the correct state and that the
- * correct intent is withdrawn from the IntentService.
- *
- * @throws TestUtilsException
- */
- @Test
- public void testFibDelete() throws TestUtilsException {
- // Firstly add a route
- testFibAdd();
-
- Intent addedIntent =
- intentSynchronizer.getRouteIntents().iterator().next();
-
- // Construct the existing route entry
- FibEntry fibEntry = new FibEntry(
- Ip4Prefix.valueOf("1.1.1.0/24"), null, null);
-
- // Set up expectation
- reset(intentService);
- // Setup the expected intents
- intentService.withdraw(eqExceptId(addedIntent));
- replay(intentService);
-
- // Call the update() method in IntentSynchronizer class
- intentSynchronizer.leaderChanged(true);
- TestUtils.setField(intentSynchronizer, "isActivatedLeader", true);
- FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.DELETE, fibEntry);
- intentSynchronizer.update(Collections.emptyList(),
- Collections.singletonList(fibUpdate));
-
- // Verify
- assertEquals(intentSynchronizer.getRouteIntents().size(), 0);
- verify(intentService);
- }
-
- /**
- * This method tests the behavior of intent Synchronizer.
+ * Tests the synchronization behavior of intent synchronizer. We set up
+ * a discrepancy between the intent service state and the intent
+ * synchronizer's state and ensure that this is reconciled correctly.
*
* @throws TestUtilsException
*/
@@ -529,27 +215,13 @@ public class IntentSyncTest extends AbstractIntentTest {
// Compose a intent, which is equal to intent5 but the id is different.
MultiPointToSinglePointIntent intent5New =
staticIntentBuilder(intent5, routeEntry5, "00:00:00:00:00:01");
- assertThat(IntentSynchronizer.IntentKey.equalIntents(
- intent5, intent5New),
- is(true));
+ assertThat(IntentUtils.equals(intent5, intent5New), is(true));
assertFalse(intent5.equals(intent5New));
MultiPointToSinglePointIntent intent6 = intentBuilder(
routeEntry6.prefix(), "00:00:00:00:00:01", SW1_ETH1);
- // Set up the routeIntents field in IntentSynchronizer class
- ConcurrentHashMap<IpPrefix, MultiPointToSinglePointIntent>
- routeIntents = new ConcurrentHashMap<>();
- routeIntents.put(routeEntry1.prefix(), intent1);
- routeIntents.put(routeEntry3.prefix(), intent3);
- routeIntents.put(routeEntry4Update.prefix(), intent4Update);
- routeIntents.put(routeEntry5.prefix(), intent5New);
- routeIntents.put(routeEntry6.prefix(), intent6);
- routeIntents.put(routeEntry7.prefix(), intent7);
- TestUtils.setField(intentSynchronizer, "routeIntents", routeIntents);
-
// Set up expectation
- reset(intentService);
Set<Intent> intents = new HashSet<>();
intents.add(intent1);
expect(intentService.getIntentState(intent1.key()))
@@ -568,9 +240,9 @@ public class IntentSyncTest extends AbstractIntentTest {
.andReturn(IntentState.WITHDRAWING).anyTimes();
expect(intentService.getIntents()).andReturn(intents).anyTimes();
+ // These are the operations that should be done to the intentService
+ // during synchronization
intentService.withdraw(intent2);
- intentService.withdraw(intent4);
-
intentService.submit(intent3);
intentService.submit(intent4Update);
intentService.submit(intent6);
@@ -578,16 +250,101 @@ public class IntentSyncTest extends AbstractIntentTest {
replay(intentService);
// Start the test
+
+ // Simulate some input from the clients. The intent synchronizer has not
+ // gained the global leadership yet, but it will remember this input for
+ // when it does.
+ intentSynchronizer.submit(intent1);
+ intentSynchronizer.submit(intent2);
+ intentSynchronizer.withdraw(intent2);
+ intentSynchronizer.submit(intent3);
+ intentSynchronizer.submit(intent4);
+ intentSynchronizer.submit(intent4Update);
+ intentSynchronizer.submit(intent5);
+ intentSynchronizer.submit(intent6);
+ intentSynchronizer.submit(intent7);
+
+ // Give the leadership to the intent synchronizer. It will now attempt
+ // to synchronize the intents in the store with the intents it has
+ // recorded based on the earlier user input.
+ intentSynchronizer.leaderChanged(true);
+
+ verify(intentService);
+ }
+
+ /**
+ * Tests the behavior of the submit API, both when the synchronizer has
+ * leadership and when it does not.
+ */
+ @Test
+ public void testSubmit() {
+ IpPrefix prefix = Ip4Prefix.valueOf("1.1.1.0/24");
+ Intent intent = intentBuilder(prefix, "00:00:00:00:00:01", SW1_ETH1);
+
+ // Set up expectations
+ intentService.submit(intent);
+ expect(intentService.getIntents()).andReturn(Collections.emptyList())
+ .anyTimes();
+ replay(intentService);
+
+ // Give the intent synchronizer leadership so it will submit intents
+ // to the intent service
+ intentSynchronizer.leaderChanged(true);
+
+ // Test the submit
+ intentSynchronizer.submit(intent);
+
+ verify(intentService);
+
+ // Now we'll remove leadership from the intent synchronizer and verify
+ // that it does not submit any intents to the intent service when we
+ // call the submit API
+ reset(intentService);
+ replay(intentService);
+
+ intentSynchronizer.leaderChanged(false);
+
+ intentSynchronizer.submit(intent);
+
+ verify(intentService);
+ }
+
+ /**
+ * Tests the behavior of the withdraw API, both when the synchronizer has
+ * leadership and when it does not.
+ */
+ @Test
+ public void testWithdraw() {
+ IpPrefix prefix = Ip4Prefix.valueOf("1.1.1.0/24");
+ Intent intent = intentBuilder(prefix, "00:00:00:00:00:01", SW1_ETH1);
+
+ // Submit an intent first so we can withdraw it later
+ intentService.submit(intent);
+ intentService.withdraw(intent);
+ expect(intentService.getIntents()).andReturn(Collections.emptyList())
+ .anyTimes();
+ replay(intentService);
+
+ // Give the intent synchronizer leadership so it will submit intents
+ // to the intent service
intentSynchronizer.leaderChanged(true);
- intentSynchronizer.synchronizeIntents();
- // Verify
- assertEquals(intentSynchronizer.getRouteIntents().size(), 6);
- assertTrue(intentSynchronizer.getRouteIntents().contains(intent1));
- assertTrue(intentSynchronizer.getRouteIntents().contains(intent3));
- assertTrue(intentSynchronizer.getRouteIntents().contains(intent4Update));
- assertTrue(intentSynchronizer.getRouteIntents().contains(intent5));
- assertTrue(intentSynchronizer.getRouteIntents().contains(intent6));
+ // Test the submit then withdraw
+ intentSynchronizer.submit(intent);
+ intentSynchronizer.withdraw(intent);
+
+ verify(intentService);
+
+ // Now we'll remove leadership from the intent synchronizer and verify
+ // that it does not withdraw any intents to the intent service when we
+ // call the withdraw API
+ reset(intentService);
+ replay(intentService);
+
+ intentSynchronizer.leaderChanged(false);
+
+ intentSynchronizer.submit(intent);
+ intentSynchronizer.withdraw(intent);
verify(intentService);
}
@@ -607,10 +364,10 @@ public class IntentSyncTest extends AbstractIntentTest {
TrafficSelector.Builder selectorBuilder =
DefaultTrafficSelector.builder();
if (ipPrefix.isIp4()) {
- selectorBuilder.matchEthType(Ethernet.TYPE_IPV4); // IPv4
+ selectorBuilder.matchEthType(Ethernet.TYPE_IPV4);
selectorBuilder.matchIPDst(ipPrefix);
} else {
- selectorBuilder.matchEthType(Ethernet.TYPE_IPV6); // IPv6
+ selectorBuilder.matchEthType(Ethernet.TYPE_IPV6);
selectorBuilder.matchIPv6Dst(ipPrefix);
}
@@ -628,11 +385,12 @@ public class IntentSyncTest extends AbstractIntentTest {
MultiPointToSinglePointIntent intent =
MultiPointToSinglePointIntent.builder()
.appId(APPID)
+ .key(Key.of(ipPrefix.toString(), APPID))
.selector(selectorBuilder.build())
.treatment(treatmentBuilder.build())
.ingressPoints(ingressPoints)
.egressPoint(egressPoint)
- .constraints(IntentSynchronizer.CONSTRAINTS)
+ .constraints(SdnIpFib.CONSTRAINTS)
.build();
return intent;
}
@@ -646,7 +404,7 @@ public class IntentSyncTest extends AbstractIntentTest {
* @return the newly constructed MultiPointToSinglePointIntent
* @throws TestUtilsException
*/
- private MultiPointToSinglePointIntent staticIntentBuilder(
+ private MultiPointToSinglePointIntent staticIntentBuilder(
MultiPointToSinglePointIntent intent, RouteEntry routeEntry,
String nextHopMacAddress) throws TestUtilsException {
diff --git a/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/PeerConnectivityManagerTest.java b/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/PeerConnectivityManagerTest.java
index d89c3c2b..c4b2daad 100644
--- a/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/PeerConnectivityManagerTest.java
+++ b/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/PeerConnectivityManagerTest.java
@@ -19,7 +19,6 @@ import com.google.common.collect.Sets;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
-import org.onlab.junit.TestUtils;
import org.onlab.junit.TestUtils.TestUtilsException;
import org.onlab.packet.Ethernet;
import org.onlab.packet.IPv4;
@@ -28,13 +27,14 @@ import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.packet.TpPort;
import org.onlab.packet.VlanId;
+import org.onosproject.TestApplicationId;
import org.onosproject.core.ApplicationId;
-import org.onosproject.net.config.NetworkConfigService;
import org.onosproject.incubator.net.intf.Interface;
import org.onosproject.incubator.net.intf.InterfaceService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
+import org.onosproject.net.config.NetworkConfigService;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.TrafficSelector;
@@ -42,8 +42,9 @@ import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.host.InterfaceIpAddress;
import org.onosproject.net.intent.AbstractIntentTest;
import org.onosproject.net.intent.Intent;
-import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.Key;
import org.onosproject.net.intent.PointToPointIntent;
+import org.onosproject.routing.IntentSynchronizationService;
import org.onosproject.routing.config.BgpConfig;
import org.onosproject.routing.config.BgpPeer;
import org.onosproject.routing.config.BgpSpeaker;
@@ -71,26 +72,15 @@ import static org.onosproject.sdnip.TestIntentServiceHelper.eqExceptId;
*/
public class PeerConnectivityManagerTest extends AbstractIntentTest {
- private static final ApplicationId APPID = new ApplicationId() {
- @Override
- public short id() {
- return 0;
- }
-
- @Override
- public String name() {
- return "foo";
- }
- };
+ private static final ApplicationId APPID = TestApplicationId.create("foo");
private static final ApplicationId CONFIG_APP_ID = APPID;
private PeerConnectivityManager peerConnectivityManager;
- private IntentSynchronizer intentSynchronizer;
+ private IntentSynchronizationService intentSynchronizer;
private RoutingConfigurationService routingConfig;
private InterfaceService interfaceService;
private NetworkConfigService networkConfigService;
- private IntentService intentService;
private Set<BgpConfig.BgpSpeakerConfig> bgpSpeakers;
private Map<String, Interface> interfaces;
@@ -98,8 +88,6 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
private BgpConfig bgpConfig;
- private Map<String, Interface> configuredInterfaces;
- private Map<IpAddress, BgpPeer> configuredPeers;
private List<PointToPointIntent> intentList;
private final String dpid1 = "00:00:00:00:00:00:00:01";
@@ -136,7 +124,7 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
// These will set expectations on routingConfig and interfaceService
bgpSpeakers = setUpBgpSpeakers();
interfaces = Collections.unmodifiableMap(setUpInterfaces());
- peers = Collections.unmodifiableMap(setUpPeers());
+ peers = setUpPeers();
initPeerConnectivity();
intentList = setUpIntentList();
@@ -169,11 +157,11 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
* Sets up logical interfaces, which emulate the configured interfaces
* in SDN-IP application.
*
- * @return configured interfaces as a MAP from Interface name to Interface
+ * @return configured interfaces as a map from interface name to Interface
*/
private Map<String, Interface> setUpInterfaces() {
- configuredInterfaces = new HashMap<>();
+ Map<String, Interface> configuredInterfaces = new HashMap<>();
String interfaceSw1Eth1 = "s1-eth1";
InterfaceIpAddress ia1 =
@@ -242,7 +230,7 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
*/
private Map<IpAddress, BgpPeer> setUpPeers() {
- configuredPeers = new HashMap<>();
+ Map<IpAddress, BgpPeer> configuredPeers = new HashMap<>();
String peerSw1Eth1 = "192.168.10.1";
configuredPeers.put(IpAddress.valueOf(peerSw1Eth1),
@@ -266,14 +254,12 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
* @return point to point intent list
*/
private List<PointToPointIntent> setUpIntentList() {
-
intentList = new ArrayList<>();
setUpBgpIntents();
setUpIcmpIntents();
return intentList;
-
}
/**
@@ -306,8 +292,12 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
builder.matchTcpDst(TpPort.tpPort(dstTcpPort));
}
+ Key key = Key.of(srcPrefix.split("/")[0] + "-" + dstPrefix.split("/")[0]
+ + "-" + ((srcTcpPort == null) ? "dst" : "src"), APPID);
+
PointToPointIntent intent = PointToPointIntent.builder()
.appId(APPID)
+ .key(key)
.selector(builder.build())
.treatment(noTreatment)
.ingressPoint(srcConnectPoint)
@@ -392,8 +382,12 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
.matchIPDst(IpPrefix.valueOf(dstPrefix))
.build();
+ Key key = Key.of(srcPrefix.split("/")[0] + "-" + dstPrefix.split("/")[0]
+ + "-" + "icmp", APPID);
+
PointToPointIntent intent = PointToPointIntent.builder()
.appId(APPID)
+ .key(key)
.selector(selector)
.treatment(noTreatment)
.ingressPoint(srcConnectPoint)
@@ -434,19 +428,14 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
expect(routingConfig.getBgpPeers()).andReturn(peers).anyTimes();
expect(bgpConfig.bgpSpeakers()).andReturn(bgpSpeakers).anyTimes();
replay(bgpConfig);
- expect(networkConfigService.getConfig(APPID, BgpConfig.class)).andReturn(bgpConfig).anyTimes();
+ expect(networkConfigService.getConfig(APPID, BgpConfig.class))
+ .andReturn(bgpConfig).anyTimes();
replay(networkConfigService);
replay(routingConfig);
replay(interfaceService);
- intentService = createMock(IntentService.class);
- replay(intentService);
-
- intentSynchronizer = new IntentSynchronizer(APPID, intentService,
- null, routingConfig,
- interfaceService);
- intentSynchronizer.leaderChanged(true);
- TestUtils.setField(intentSynchronizer, "isActivatedLeader", true);
+ intentSynchronizer = createMock(IntentSynchronizationService.class);
+ replay(intentSynchronizer);
peerConnectivityManager =
new PeerConnectivityManager(APPID, intentSynchronizer,
@@ -464,20 +453,18 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
*/
@Test
public void testConnectionSetup() {
-
- reset(intentService);
+ reset(intentSynchronizer);
// Setup the expected intents
for (Intent intent : intentList) {
- intentService.submit(eqExceptId(intent));
+ intentSynchronizer.submit(eqExceptId(intent));
}
- replay(intentService);
+ replay(intentSynchronizer);
// Running the interface to be tested.
peerConnectivityManager.start();
- verify(intentService);
-
+ verify(intentSynchronizer);
}
/**
@@ -488,7 +475,7 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
reset(interfaceService);
expect(interfaceService.getInterfaces()).andReturn(
- Sets.<Interface>newHashSet()).anyTimes();
+ Sets.newHashSet()).anyTimes();
expect(interfaceService.getInterfacesByPort(s2Eth1))
.andReturn(Collections.emptySet()).anyTimes();
expect(interfaceService.getInterfacesByPort(s1Eth1))
@@ -508,10 +495,10 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
replay(interfaceService);
- reset(intentService);
- replay(intentService);
+ reset(intentSynchronizer);
+ replay(intentSynchronizer);
peerConnectivityManager.start();
- verify(intentService);
+ verify(intentSynchronizer);
}
/**
@@ -527,10 +514,10 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
expect(routingConfig.getBgpPeers()).andReturn(peers).anyTimes();
replay(routingConfig);
- reset(intentService);
- replay(intentService);
+ reset(intentSynchronizer);
+ replay(intentSynchronizer);
peerConnectivityManager.start();
- verify(intentService);
+ verify(intentSynchronizer);
}
/**
@@ -540,7 +527,7 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest {
@Test
public void testNoPeerInterface() {
String peerSw100Eth1 = "192.168.200.1";
- configuredPeers.put(IpAddress.valueOf(peerSw100Eth1),
+ peers.put(IpAddress.valueOf(peerSw100Eth1),
new BgpPeer("00:00:00:00:00:00:01:00", 1, peerSw100Eth1));
testConnectionSetup();
}
diff --git a/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/SdnIpFibTest.java b/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/SdnIpFibTest.java
new file mode 100644
index 00000000..5466d520
--- /dev/null
+++ b/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/SdnIpFibTest.java
@@ -0,0 +1,417 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.sdnip;
+
+import com.google.common.collect.Sets;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.TestApplicationId;
+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.DeviceId;
+import org.onosproject.net.PortNumber;
+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.host.InterfaceIpAddress;
+import org.onosproject.net.intent.AbstractIntentTest;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.MultiPointToSinglePointIntent;
+import org.onosproject.routing.FibEntry;
+import org.onosproject.routing.FibUpdate;
+import org.onosproject.routing.IntentSynchronizationService;
+import org.onosproject.routing.config.BgpPeer;
+import org.onosproject.routing.config.RoutingConfigurationService;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.reset;
+import static org.easymock.EasyMock.verify;
+import static org.onosproject.sdnip.TestIntentServiceHelper.eqExceptId;
+
+/**
+ * Unit tests for SdnIpFib.
+ */
+public class SdnIpFibTest extends AbstractIntentTest {
+
+ private RoutingConfigurationService routingConfig;
+ private InterfaceService interfaceService;
+
+ private static final ConnectPoint SW1_ETH1 = new ConnectPoint(
+ DeviceId.deviceId("of:0000000000000001"),
+ PortNumber.portNumber(1));
+
+ private static final ConnectPoint SW2_ETH1 = new ConnectPoint(
+ DeviceId.deviceId("of:0000000000000002"),
+ PortNumber.portNumber(1));
+
+ private static final ConnectPoint SW3_ETH1 = new ConnectPoint(
+ DeviceId.deviceId("of:0000000000000003"),
+ PortNumber.portNumber(1));
+
+ private static final ConnectPoint SW4_ETH1 = new ConnectPoint(
+ DeviceId.deviceId("of:0000000000000004"),
+ PortNumber.portNumber(1));
+
+ private SdnIpFib sdnipFib;
+ private IntentSynchronizationService intentSynchronizer;
+ private final Set<Interface> interfaces = Sets.newHashSet();
+
+ private static final ApplicationId APPID = TestApplicationId.create("SDNIP");
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ routingConfig = createMock(RoutingConfigurationService.class);
+ interfaceService = createMock(InterfaceService.class);
+
+ // These will set expectations on routingConfig and interfaceService
+ setUpInterfaceService();
+ setUpBgpPeers();
+
+ replay(routingConfig);
+ replay(interfaceService);
+
+ intentSynchronizer = createMock(IntentSynchronizationService.class);
+
+ sdnipFib = new SdnIpFib(APPID, interfaceService, intentSynchronizer);
+ }
+
+ /**
+ * Sets up BGP peers in external networks.
+ */
+ private void setUpBgpPeers() {
+
+ Map<IpAddress, BgpPeer> peers = new HashMap<>();
+
+ String peerSw1Eth1 = "192.168.10.1";
+ peers.put(IpAddress.valueOf(peerSw1Eth1),
+ new BgpPeer("00:00:00:00:00:00:00:01", 1, peerSw1Eth1));
+
+ // Two BGP peers are connected to switch 2 port 1.
+ String peer1Sw2Eth1 = "192.168.20.1";
+ peers.put(IpAddress.valueOf(peer1Sw2Eth1),
+ new BgpPeer("00:00:00:00:00:00:00:02", 1, peer1Sw2Eth1));
+
+ String peer2Sw2Eth1 = "192.168.20.2";
+ peers.put(IpAddress.valueOf(peer2Sw2Eth1),
+ new BgpPeer("00:00:00:00:00:00:00:02", 1, peer2Sw2Eth1));
+
+ String peer1Sw4Eth1 = "192.168.40.1";
+ peers.put(IpAddress.valueOf(peer1Sw4Eth1),
+ new BgpPeer("00:00:00:00:00:00:00:04", 1, peer1Sw4Eth1));
+
+ expect(routingConfig.getBgpPeers()).andReturn(peers).anyTimes();
+ }
+
+ /**
+ * Sets up InterfaceService.
+ */
+ private void setUpInterfaceService() {
+ Set<InterfaceIpAddress> interfaceIpAddresses1 = Sets.newHashSet();
+ interfaceIpAddresses1.add(new InterfaceIpAddress(
+ IpAddress.valueOf("192.168.10.101"),
+ IpPrefix.valueOf("192.168.10.0/24")));
+ Interface sw1Eth1 = new Interface(SW1_ETH1,
+ interfaceIpAddresses1, MacAddress.valueOf("00:00:00:00:00:01"),
+ VlanId.NONE);
+ interfaces.add(sw1Eth1);
+
+ Set<InterfaceIpAddress> interfaceIpAddresses2 = Sets.newHashSet();
+ interfaceIpAddresses2.add(
+ new InterfaceIpAddress(IpAddress.valueOf("192.168.20.101"),
+ IpPrefix.valueOf("192.168.20.0/24")));
+ Interface sw2Eth1 = new Interface(SW2_ETH1,
+ interfaceIpAddresses2, MacAddress.valueOf("00:00:00:00:00:02"),
+ VlanId.NONE);
+ interfaces.add(sw2Eth1);
+
+ Set<InterfaceIpAddress> interfaceIpAddresses3 = Sets.newHashSet();
+ interfaceIpAddresses3.add(
+ new InterfaceIpAddress(IpAddress.valueOf("192.168.30.101"),
+ IpPrefix.valueOf("192.168.30.0/24")));
+ Interface sw3Eth1 = new Interface(SW3_ETH1,
+ interfaceIpAddresses3, MacAddress.valueOf("00:00:00:00:00:03"),
+ VlanId.NONE);
+ interfaces.add(sw3Eth1);
+
+ InterfaceIpAddress interfaceIpAddress4 =
+ new InterfaceIpAddress(IpAddress.valueOf("192.168.40.101"),
+ IpPrefix.valueOf("192.168.40.0/24"));
+ Interface sw4Eth1 = new Interface(SW4_ETH1,
+ Sets.newHashSet(interfaceIpAddress4),
+ MacAddress.valueOf("00:00:00:00:00:04"),
+ VlanId.vlanId((short) 1));
+
+ expect(interfaceService.getInterfacesByPort(SW4_ETH1)).andReturn(
+ Collections.singleton(sw4Eth1)).anyTimes();
+ expect(interfaceService.getMatchingInterface(Ip4Address.valueOf("192.168.40.1")))
+ .andReturn(sw4Eth1).anyTimes();
+
+ interfaces.add(sw4Eth1);
+
+ expect(interfaceService.getInterfacesByPort(SW1_ETH1)).andReturn(
+ Collections.singleton(sw1Eth1)).anyTimes();
+ expect(interfaceService.getMatchingInterface(Ip4Address.valueOf("192.168.10.1")))
+ .andReturn(sw1Eth1).anyTimes();
+ expect(interfaceService.getInterfacesByPort(SW2_ETH1)).andReturn(
+ Collections.singleton(sw2Eth1)).anyTimes();
+ expect(interfaceService.getMatchingInterface(Ip4Address.valueOf("192.168.20.1")))
+ .andReturn(sw2Eth1).anyTimes();
+ expect(interfaceService.getInterfacesByPort(SW3_ETH1)).andReturn(
+ Collections.singleton(sw3Eth1)).anyTimes();
+ expect(interfaceService.getMatchingInterface(Ip4Address.valueOf("192.168.30.1")))
+ .andReturn(sw3Eth1).anyTimes();
+ expect(interfaceService.getInterfaces()).andReturn(interfaces).anyTimes();
+ }
+
+ /**
+ * Tests adding a FIB entry to the IntentSynchronizer.
+ *
+ * We verify that the synchronizer records the correct state and that the
+ * correct intent is submitted to the IntentService.
+ */
+ @Test
+ public void testFibAdd() {
+ IpPrefix prefix = Ip4Prefix.valueOf("1.1.1.0/24");
+ FibEntry fibEntry = new FibEntry(prefix,
+ Ip4Address.valueOf("192.168.10.1"),
+ MacAddress.valueOf("00:00:00:00:00:01"));
+
+ // Construct a MultiPointToSinglePointIntent intent
+ TrafficSelector.Builder selectorBuilder =
+ DefaultTrafficSelector.builder();
+ selectorBuilder.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(
+ fibEntry.prefix());
+
+ TrafficTreatment.Builder treatmentBuilder =
+ DefaultTrafficTreatment.builder();
+ treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:01"));
+
+ Set<ConnectPoint> ingressPoints = new HashSet<>();
+ ingressPoints.add(SW2_ETH1);
+ ingressPoints.add(SW3_ETH1);
+ ingressPoints.add(SW4_ETH1);
+
+ MultiPointToSinglePointIntent intent =
+ MultiPointToSinglePointIntent.builder()
+ .appId(APPID)
+ .key(Key.of(prefix.toString(), APPID))
+ .selector(selectorBuilder.build())
+ .treatment(treatmentBuilder.build())
+ .ingressPoints(ingressPoints)
+ .egressPoint(SW1_ETH1)
+ .constraints(SdnIpFib.CONSTRAINTS)
+ .build();
+
+ // Setup the expected intents
+ intentSynchronizer.submit(eqExceptId(intent));
+ replay(intentSynchronizer);
+
+ // Send in the UPDATE FibUpdate
+ FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.UPDATE, fibEntry);
+ sdnipFib.update(Collections.singleton(fibUpdate), Collections.emptyList());
+
+ verify(intentSynchronizer);
+ }
+
+ /**
+ * Tests adding a FIB entry with to a next hop in a VLAN.
+ *
+ * We verify that the synchronizer records the correct state and that the
+ * correct intent is submitted to the IntentService.
+ */
+ @Test
+ public void testFibAddWithVlan() {
+ IpPrefix prefix = Ip4Prefix.valueOf("3.3.3.0/24");
+ FibEntry fibEntry = new FibEntry(prefix,
+ Ip4Address.valueOf("192.168.40.1"),
+ MacAddress.valueOf("00:00:00:00:00:04"));
+
+ // Construct a MultiPointToSinglePointIntent intent
+ TrafficSelector.Builder selectorBuilder =
+ DefaultTrafficSelector.builder();
+ selectorBuilder.matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPDst(fibEntry.prefix())
+ .matchVlanId(VlanId.ANY);
+
+ TrafficTreatment.Builder treatmentBuilder =
+ DefaultTrafficTreatment.builder();
+ treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:04"))
+ .setVlanId(VlanId.vlanId((short) 1));
+
+ Set<ConnectPoint> ingressPoints = new HashSet<>();
+ ingressPoints.add(SW1_ETH1);
+ ingressPoints.add(SW2_ETH1);
+ ingressPoints.add(SW3_ETH1);
+
+ MultiPointToSinglePointIntent intent =
+ MultiPointToSinglePointIntent.builder()
+ .appId(APPID)
+ .key(Key.of(prefix.toString(), APPID))
+ .selector(selectorBuilder.build())
+ .treatment(treatmentBuilder.build())
+ .ingressPoints(ingressPoints)
+ .egressPoint(SW4_ETH1)
+ .constraints(SdnIpFib.CONSTRAINTS)
+ .build();
+
+ // Setup the expected intents
+ intentSynchronizer.submit(eqExceptId(intent));
+
+ replay(intentSynchronizer);
+
+ // Send in the UPDATE FibUpdate
+ FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.UPDATE, fibEntry);
+ sdnipFib.update(Collections.singleton(fibUpdate), Collections.emptyList());
+
+ verify(intentSynchronizer);
+ }
+
+ /**
+ * Tests updating a FIB entry.
+ *
+ * We verify that the synchronizer records the correct state and that the
+ * correct intent is submitted to the IntentService.
+ */
+ @Test
+ public void testFibUpdate() {
+ // Firstly add a route
+ testFibAdd();
+
+ IpPrefix prefix = Ip4Prefix.valueOf("1.1.1.0/24");
+
+ // Start to construct a new route entry and new intent
+ FibEntry fibEntryUpdate = new FibEntry(prefix,
+ Ip4Address.valueOf("192.168.20.1"),
+ MacAddress.valueOf("00:00:00:00:00:02"));
+
+ // Construct a new MultiPointToSinglePointIntent intent
+ TrafficSelector.Builder selectorBuilderNew =
+ DefaultTrafficSelector.builder();
+ selectorBuilderNew.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(
+ fibEntryUpdate.prefix());
+
+ TrafficTreatment.Builder treatmentBuilderNew =
+ DefaultTrafficTreatment.builder();
+ treatmentBuilderNew.setEthDst(MacAddress.valueOf("00:00:00:00:00:02"));
+
+ Set<ConnectPoint> ingressPointsNew = new HashSet<>();
+ ingressPointsNew.add(SW1_ETH1);
+ ingressPointsNew.add(SW3_ETH1);
+ ingressPointsNew.add(SW4_ETH1);
+
+ MultiPointToSinglePointIntent intentNew =
+ MultiPointToSinglePointIntent.builder()
+ .appId(APPID)
+ .key(Key.of(prefix.toString(), APPID))
+ .selector(selectorBuilderNew.build())
+ .treatment(treatmentBuilderNew.build())
+ .ingressPoints(ingressPointsNew)
+ .egressPoint(SW2_ETH1)
+ .constraints(SdnIpFib.CONSTRAINTS)
+ .build();
+
+ // Set up test expectation
+ reset(intentSynchronizer);
+
+ // Setup the expected intents
+ intentSynchronizer.submit(eqExceptId(intentNew));
+ replay(intentSynchronizer);
+
+ // Send in the UPDATE FibUpdate
+ FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.UPDATE,
+ fibEntryUpdate);
+ sdnipFib.update(Collections.singletonList(fibUpdate),
+ Collections.emptyList());
+
+ verify(intentSynchronizer);
+ }
+
+ /**
+ * Tests deleting a FIB entry.
+ *
+ * We verify that the synchronizer records the correct state and that the
+ * correct intent is withdrawn from the IntentService.
+ */
+ @Test
+ public void testFibDelete() {
+ // Firstly add a route
+ testFibAdd();
+
+ IpPrefix prefix = Ip4Prefix.valueOf("1.1.1.0/24");
+
+ // Construct the existing route entry
+ FibEntry fibEntry = new FibEntry(prefix, null, null);
+
+ // Construct the existing MultiPointToSinglePoint intent
+ TrafficSelector.Builder selectorBuilder =
+ DefaultTrafficSelector.builder();
+ selectorBuilder.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(
+ fibEntry.prefix());
+
+ TrafficTreatment.Builder treatmentBuilder =
+ DefaultTrafficTreatment.builder();
+ treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:01"));
+
+ Set<ConnectPoint> ingressPoints = new HashSet<>();
+ ingressPoints.add(SW2_ETH1);
+ ingressPoints.add(SW3_ETH1);
+ ingressPoints.add(SW4_ETH1);
+
+ MultiPointToSinglePointIntent addedIntent =
+ MultiPointToSinglePointIntent.builder()
+ .appId(APPID)
+ .key(Key.of(prefix.toString(), APPID))
+ .selector(selectorBuilder.build())
+ .treatment(treatmentBuilder.build())
+ .ingressPoints(ingressPoints)
+ .egressPoint(SW1_ETH1)
+ .constraints(SdnIpFib.CONSTRAINTS)
+ .build();
+
+ // Set up expectation
+ reset(intentSynchronizer);
+ // Setup the expected intents
+ intentSynchronizer.withdraw(eqExceptId(addedIntent));
+ replay(intentSynchronizer);
+
+ // Send in the DELETE FibUpdate
+ FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.DELETE, fibEntry);
+ sdnipFib.update(Collections.emptyList(), Collections.singletonList(fibUpdate));
+
+ verify(intentSynchronizer);
+ }
+}
diff --git a/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/TestIntentServiceHelper.java b/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/TestIntentServiceHelper.java
index 69b18aa9..7f825e81 100644
--- a/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/TestIntentServiceHelper.java
+++ b/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/TestIntentServiceHelper.java
@@ -17,7 +17,6 @@ package org.onosproject.sdnip;
import org.easymock.IArgumentMatcher;
import org.onosproject.net.intent.Intent;
-import org.onosproject.sdnip.IntentSynchronizer.IntentKey;
import static org.easymock.EasyMock.reportMatcher;
@@ -53,8 +52,6 @@ public final class TestIntentServiceHelper {
* the solution is to use an EasyMock matcher that verifies that all the
* value properties of the provided intent match the expected values, but
* ignores the intent ID when testing equality.
- *
- * FIXME this currently does not take key into account
*/
private static final class IdAgnosticIntentMatcher implements
IArgumentMatcher {
@@ -86,9 +83,7 @@ public final class TestIntentServiceHelper {
Intent providedIntent = (Intent) object;
providedString = providedIntent.toString();
- IntentKey thisIntentKey = new IntentKey(intent);
- IntentKey providedIntentKey = new IntentKey(providedIntent);
- return thisIntentKey.equals(providedIntentKey);
+ return IntentUtils.equals(intent, providedIntent);
}
}