Added suggest path to PointToPointIntent
Change-Id: Ie8ae3af6bd97af3628334d37482e63196d15b094
diff --git a/core/api/src/main/java/org/onosproject/net/intent/PointToPointIntent.java b/core/api/src/main/java/org/onosproject/net/intent/PointToPointIntent.java
index 1a38bfd..d0a9d54 100644
--- a/core/api/src/main/java/org/onosproject/net/intent/PointToPointIntent.java
+++ b/core/api/src/main/java/org/onosproject/net/intent/PointToPointIntent.java
@@ -19,6 +19,7 @@
import com.google.common.base.MoreObjects;
import org.onosproject.core.ApplicationId;
import org.onosproject.net.FilteredConnectPoint;
+import org.onosproject.net.Link;
import org.onosproject.net.ResourceGroup;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
@@ -38,6 +39,8 @@
private final FilteredConnectPoint ingressPoint;
private final FilteredConnectPoint egressPoint;
+ private final List<Link> suggestedPath;
+
/**
* Returns a new point to point intent builder. The application id,
* ingress point and egress point are required fields. If they are
@@ -57,6 +60,8 @@
FilteredConnectPoint ingressPoint;
FilteredConnectPoint egressPoint;
+ List<Link> suggestedPath;
+
private Builder() {
// Hide constructor
}
@@ -121,12 +126,36 @@
}
/**
+ * Sets the suggested path as list of links.
+ *
+ * @param links list of suggested links
+ * @return this builder
+ */
+ public Builder suggestedPath(List<Link> links) {
+ this.suggestedPath = links;
+ return this;
+ }
+
+ /**
* Builds a point to point intent from the accumulated parameters.
*
* @return point to point intent
*/
public PointToPointIntent build() {
-
+ if (suggestedPath != null &&
+ suggestedPath.size() > 0 &&
+ (!suggestedPath.get(0)
+ .src()
+ .deviceId()
+ .equals(ingressPoint.connectPoint().deviceId()) ||
+ !suggestedPath.get(suggestedPath.size() - 1)
+ .dst()
+ .deviceId()
+ .equals(egressPoint.connectPoint().deviceId()))
+ ) {
+ throw new IllegalArgumentException(
+ "Suggested path not compatible with ingress and egress connect points");
+ }
return new PointToPointIntent(
appId,
key,
@@ -136,6 +165,7 @@
egressPoint,
constraints,
priority,
+ suggestedPath,
resourceGroup
);
}
@@ -147,16 +177,17 @@
* Creates a new point-to-point intent with the supplied ingress/egress
* ports and constraints.
*
- * @param appId application identifier
- * @param key key of the intent
- * @param selector traffic selector
- * @param treatment treatment
- * @param ingressPoint filtered ingress port
- * @param egressPoint filtered egress port
- * @param constraints optional list of constraints
- * @param priority priority to use for flows generated by this intent
+ * @param appId application identifier
+ * @param key key of the intent
+ * @param selector traffic selector
+ * @param treatment treatment
+ * @param ingressPoint filtered ingress port
+ * @param egressPoint filtered egress port
+ * @param constraints optional list of constraints
+ * @param priority priority to use for flows generated by this intent
+ * @param suggestedPath suggested path
* @throws NullPointerException if {@code ingressPoint} or
- * {@code egressPoints} or {@code appId} is null.
+ * {@code egressPoints} or {@code appId} is null or {@code path} is null.
*/
private PointToPointIntent(ApplicationId appId,
Key key,
@@ -166,15 +197,24 @@
FilteredConnectPoint egressPoint,
List<Constraint> constraints,
int priority,
+ List<Link> suggestedPath,
ResourceGroup resourceGroup) {
- super(appId, key, Collections.emptyList(), selector, treatment, constraints,
- priority, resourceGroup);
+ super(appId,
+ key,
+ suggestedPath != null ? resources(suggestedPath) : Collections.emptyList(),
+ selector,
+ treatment,
+ constraints,
+ priority,
+ resourceGroup);
checkArgument(!ingressPoint.equals(egressPoint),
"ingress and egress should be different (ingress: %s, egress: %s)", ingressPoint, egressPoint);
this.ingressPoint = checkNotNull(ingressPoint);
this.egressPoint = checkNotNull(egressPoint);
+ this.suggestedPath = suggestedPath;
+
}
/**
@@ -184,6 +224,7 @@
super();
this.ingressPoint = null;
this.egressPoint = null;
+ this.suggestedPath = null;
}
/**
@@ -205,6 +246,16 @@
return egressPoint;
}
+
+ /**
+ * Return the suggested path (as a list of links) that the compiler should use.
+ *
+ * @return suggested path
+ */
+ public List<Link> suggestedPath() {
+ return suggestedPath;
+ }
+
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
@@ -218,6 +269,7 @@
.add("ingress", filteredIngressPoint())
.add("egress", filteredEgressPoint())
.add("constraints", constraints())
+ .add("links", suggestedPath())
.add("resourceGroup", resourceGroup())
.toString();
}
diff --git a/core/api/src/test/java/org/onosproject/net/intent/IntentTestsMocks.java b/core/api/src/test/java/org/onosproject/net/intent/IntentTestsMocks.java
index 48b4a87..2b8c6d7 100644
--- a/core/api/src/test/java/org/onosproject/net/intent/IntentTestsMocks.java
+++ b/core/api/src/test/java/org/onosproject/net/intent/IntentTestsMocks.java
@@ -20,6 +20,8 @@
import org.onlab.graph.ScalarWeight;
import org.onlab.graph.Weight;
import org.onosproject.core.GroupId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultLink;
import org.onosproject.net.DefaultPath;
import org.onosproject.net.DeviceId;
import org.onosproject.net.ElementId;
@@ -41,6 +43,7 @@
import org.onosproject.net.flow.instructions.Instruction;
import org.onosproject.net.flow.instructions.Instructions;
import org.onosproject.net.flow.instructions.Instructions.MetadataInstruction;
+import org.onosproject.net.link.LinkServiceAdapter;
import org.onosproject.net.provider.ProviderId;
import org.onosproject.net.topology.DefaultTopologyEdge;
import org.onosproject.net.topology.DefaultTopologyVertex;
@@ -58,7 +61,10 @@
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import static org.onosproject.net.Link.Type.DIRECT;
import static org.onosproject.net.NetTestTools.*;
/**
@@ -189,6 +195,71 @@
}
/**
+ * Mock path service for creating paths within the test with multiple possible paths.
+ */
+ public static class MockMultiplePathService extends PathServiceAdapter {
+
+ final String[][] pathsHops;
+
+ /**
+ * Constructor that provides a set of hops to mock.
+ *
+ * @param pathHops multiple path hops to mock
+ */
+ public MockMultiplePathService(String[][] pathHops) {
+ this.pathsHops = pathHops;
+ }
+
+ @Override
+ public Set<Path> getPaths(ElementId src, ElementId dst) {
+
+ //Extracts all the paths that goes from src to dst
+ Set<Path> allPaths = new HashSet<>();
+ allPaths.addAll(IntStream.range(0, pathsHops.length)
+ .filter(i -> src.toString().endsWith(pathsHops[i][0])
+ && dst.toString().endsWith(pathsHops[i][pathsHops[i].length - 1]))
+ .mapToObj(i -> createPath(src instanceof HostId,
+ dst instanceof HostId,
+ pathsHops[i]))
+ .collect(Collectors.toSet()));
+
+ // Maintain only the shortest paths
+ int minPathLength = allPaths.stream()
+ .mapToInt(o -> o.links().size())
+ .min()
+ .orElse(Integer.MAX_VALUE);
+ Set<Path> shortestPaths = allPaths.stream()
+ .filter(path -> path.links().size() <= minPathLength)
+ .collect(Collectors.toSet());
+
+ return shortestPaths;
+ }
+
+
+ @Override
+ public Set<Path> getPaths(ElementId src, ElementId dst, LinkWeigher weigher) {
+ Set<Path> paths = getPaths(src, dst);
+
+ for (Path path : paths) {
+ DeviceId srcDevice = path.src().elementId() instanceof DeviceId ? path.src().deviceId() : null;
+ DeviceId dstDevice = path.dst().elementId() instanceof DeviceId ? path.dst().deviceId() : null;
+ if (srcDevice != null && dstDevice != null) {
+ TopologyVertex srcVertex = new DefaultTopologyVertex(srcDevice);
+ TopologyVertex dstVertex = new DefaultTopologyVertex(dstDevice);
+ Link link = link(src.toString(), 1, dst.toString(), 1);
+
+ Weight weightValue = weigher.weight(new DefaultTopologyEdge(srcVertex, dstVertex, link));
+ if (weightValue.isNegative()) {
+ return new HashSet<>();
+ }
+ }
+ }
+ return paths;
+ }
+ }
+
+
+ /**
* Mock path service for creating paths within the test.
*
*/
@@ -249,6 +320,22 @@
}
/**
+ * Mock active and direct link.
+ */
+ public static class FakeLink extends DefaultLink {
+
+ /**
+ * Constructor that provides source and destination of the fake link.
+ *
+ * @param src Source connect point of the fake link
+ * @param dst Destination connect point of the fake link
+ */
+ public FakeLink(ConnectPoint src, ConnectPoint dst) {
+ super(null, src, dst, DIRECT, Link.State.ACTIVE);
+ }
+ }
+
+ /**
* Mock path service for creating paths for MP2SP intent tests, returning
* pre-determined paths.
*/
@@ -319,6 +406,38 @@
}
}
+ /**
+ * Mock link service for getting links to check path availability
+ * when a suggested path is submitted.
+ */
+ public static class MockLinkService extends LinkServiceAdapter {
+ final String[][] linksHops;
+
+ /**
+ * Constructor that provides a set of links (as a list of hops).
+ *
+ * @param linksHops links to to mock (link as a set of hops)
+ */
+ public MockLinkService(String[][] linksHops) {
+ this.linksHops = linksHops;
+ }
+
+ @Override
+ public Set<Link> getLinks() {
+ return Arrays.asList(linksHops).stream()
+ .map(path -> createPath(path).links())
+ .flatMap(List::stream)
+ .collect(Collectors.toSet());
+ }
+ @Override
+ public Set<Link> getLinks(ConnectPoint connectPoint) {
+ return getLinks().stream()
+ .filter(link -> link.src().deviceId().equals(connectPoint.deviceId())
+ || link.dst().deviceId().equals(connectPoint.deviceId()))
+ .collect(Collectors.toSet());
+ }
+ }
+
private static final IntentTestsMocks.MockSelector SELECTOR =
new IntentTestsMocks.MockSelector();
private static final IntentTestsMocks.MockTreatment TREATMENT =
diff --git a/core/api/src/test/java/org/onosproject/net/intent/PointToPointIntentTest.java b/core/api/src/test/java/org/onosproject/net/intent/PointToPointIntentTest.java
index 3b170a0..c0257c0 100644
--- a/core/api/src/test/java/org/onosproject/net/intent/PointToPointIntentTest.java
+++ b/core/api/src/test/java/org/onosproject/net/intent/PointToPointIntentTest.java
@@ -17,8 +17,15 @@
import org.junit.Test;
import org.onosproject.net.FilteredConnectPoint;
+import org.onosproject.net.Link;
+import java.util.LinkedList;
+import java.util.List;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutableBaseClass;
/**
@@ -59,6 +66,34 @@
assertEquals("incorrect egress", FP2, intent.filteredEgressPoint());
}
+ @Test
+ public void suggestedPath() {
+ List<Link> suggestedPath = new LinkedList<>();
+ suggestedPath.add(new IntentTestsMocks.FakeLink(FP1.connectPoint(), FP2.connectPoint()));
+
+ PointToPointIntent intent = createWithSuggestedPath(suggestedPath);
+ assertEquals("incorrect id", APPID, intent.appId());
+ assertEquals("incorrect match", MATCH, intent.selector());
+ assertEquals("incorrect ingress", FP1, intent.filteredIngressPoint());
+ assertEquals("incorrect egress", FP2, intent.filteredEgressPoint());
+ assertEquals("incorrect suggested path", suggestedPath, intent.suggestedPath());
+
+ }
+
+ @Test
+ public void failSuggestedPath() {
+ List<Link> suggestedPath = new LinkedList<>();
+ try {
+ suggestedPath.add(new IntentTestsMocks.FakeLink(FP3.connectPoint(), FP2.connectPoint()));
+
+ createWithSuggestedPath(suggestedPath);
+ fail("Point to Point intent building with incompatible suggested path "
+ + "not throw exception.");
+ } catch (IllegalArgumentException exception) {
+ assertThat(exception.getMessage(), containsString("Suggested path not compatible"));
+ }
+ }
+
@Override
protected PointToPointIntent createOne() {
return PointToPointIntent.builder()
@@ -101,4 +136,15 @@
.filteredEgressPoint(FP2)
.build();
}
+
+ protected PointToPointIntent createWithSuggestedPath(List<Link> suggestedPath) {
+ return PointToPointIntent.builder()
+ .appId(APPID)
+ .selector(MATCH)
+ .treatment(NOP)
+ .filteredIngressPoint(FP1)
+ .filteredEgressPoint(FP2)
+ .suggestedPath(suggestedPath)
+ .build();
+ }
}
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/ConnectivityIntentCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/ConnectivityIntentCompiler.java
index a8ab9fa..6ae262c 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/ConnectivityIntentCompiler.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/ConnectivityIntentCompiler.java
@@ -163,6 +163,28 @@
}
/**
+ * Computes all the paths between two ConnectPoints.
+ *
+ * @param intent intent on which behalf path is being computed
+ * @param one start of the path
+ * @param two end of the path
+ * @return Paths between the two, or null if no path can be found
+ */
+ protected List<Path> getPaths(ConnectivityIntent intent,
+ ElementId one, ElementId two) {
+ Set<Path> paths = pathService.getPaths(one, two, weigher(intent.constraints()));
+ final List<Constraint> constraints = intent.constraints();
+ ImmutableList<Path> filtered = FluentIterable.from(paths)
+ .filter(path -> checkPath(path, constraints))
+ .toList();
+ if (filtered.isEmpty()) {
+ return null;
+ }
+
+ return filtered;
+ }
+
+ /**
* Computes a disjoint path between two ConnectPoints.
*
* @param intent intent on which behalf path is being computed
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PointToPointIntentCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PointToPointIntentCompiler.java
index acdaddd..759202f 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PointToPointIntentCompiler.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PointToPointIntentCompiler.java
@@ -16,6 +16,7 @@
package org.onosproject.net.intent.impl.compiler;
import com.google.common.collect.ImmutableSet;
+import org.apache.commons.lang3.tuple.Pair;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
@@ -75,6 +76,7 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;
+import java.util.stream.IntStream;
import java.util.stream.Stream;
import static java.util.Arrays.asList;
@@ -128,6 +130,18 @@
ConnectPoint ingressPoint = intent.filteredIngressPoint().connectPoint();
ConnectPoint egressPoint = intent.filteredEgressPoint().connectPoint();
+ //TODO: handle protected path case with suggested path!!
+ //Idea: use suggested path as primary and another path from path service as protection
+ if (intent.suggestedPath() != null && intent.suggestedPath().size() > 0) {
+ Path path = new DefaultPath(PID, intent.suggestedPath(), new ScalarWeight(1));
+ //Check intent constraints against suggested path and suggested path availability
+ if (checkPath(path, intent.constraints()) && pathAvailable(intent)) {
+ allocateIntentBandwidth(intent, path);
+ return asList(createLinkCollectionIntent(ImmutableSet.copyOf(intent.suggestedPath()),
+ DEFAULT_COST, intent));
+ }
+ }
+
if (ingressPoint.deviceId().equals(egressPoint.deviceId())) {
return createZeroHopLinkCollectionIntent(intent);
}
@@ -147,6 +161,21 @@
}
}
+ private void allocateIntentBandwidth(PointToPointIntent intent, Path path) {
+ ConnectPoint ingressCP = intent.filteredIngressPoint().connectPoint();
+ ConnectPoint egressCP = intent.filteredEgressPoint().connectPoint();
+
+ List<ConnectPoint> pathCPs =
+ path.links().stream()
+ .flatMap(l -> Stream.of(l.src(), l.dst()))
+ .collect(Collectors.toList());
+
+ pathCPs.add(ingressCP);
+ pathCPs.add(egressCP);
+
+ allocateBandwidth(intent, pathCPs);
+ }
+
private List<Intent> createZeroHopIntent(ConnectPoint ingressPoint,
ConnectPoint egressPoint,
PointToPointIntent intent) {
@@ -165,18 +194,7 @@
intent.filteredEgressPoint().connectPoint().deviceId());
// Allocate bandwidth if a bandwidth constraint is set
- ConnectPoint ingressCP = intent.filteredIngressPoint().connectPoint();
- ConnectPoint egressCP = intent.filteredEgressPoint().connectPoint();
-
- List<ConnectPoint> pathCPs =
- path.links().stream()
- .flatMap(l -> Stream.of(l.src(), l.dst()))
- .collect(Collectors.toList());
-
- pathCPs.add(ingressCP);
- pathCPs.add(egressCP);
-
- allocateBandwidth(intent, pathCPs);
+ allocateIntentBandwidth(intent, path);
return asList(createLinkCollectionIntent(ImmutableSet.copyOf(path.links()),
path.cost(),
@@ -295,19 +313,7 @@
return reusableIntents;
} else {
// Allocate bandwidth if a bandwidth constraint is set
- ConnectPoint ingressCP = intent.filteredIngressPoint().connectPoint();
- ConnectPoint egressCP = intent.filteredEgressPoint().connectPoint();
-
- List<ConnectPoint> pathCPs =
- onlyPath.links().stream()
- .flatMap(l -> Stream.of(l.src(), l.dst()))
- .collect(Collectors.toList());
-
- pathCPs.add(ingressCP);
- pathCPs.add(egressCP);
-
- // Allocate bandwidth if a bandwidth constraint is set
- allocateBandwidth(intent, pathCPs);
+ allocateIntentBandwidth(intent, onlyPath);
links.add(createEdgeLink(ingressPoint, true));
links.addAll(onlyPath.links());
@@ -698,4 +704,47 @@
groupService.addBucketsToGroup(src.deviceId(), groupKey, addBuckets, groupKey, intent.appId());
}
+
+ /**
+ * Checks suggested path availability.
+ * It checks:
+ * - single links availability;
+ * - that first and last device of the path are coherent with ingress and egress devices;
+ * - links contiguity.
+ *
+ * @param intent Intent with suggested path to check
+ * @return true if the suggested path is available
+ */
+ private boolean pathAvailable(PointToPointIntent intent) {
+ // Check links availability
+ List<Link> suggestedPath = intent.suggestedPath();
+ for (Link link : suggestedPath) {
+ if (!(link instanceof EdgeLink) && !linkService.getLinks(link.src()).contains(link)) {
+ return false;
+ }
+ }
+
+ //Check that first and last device of the path are intent ingress and egress devices
+ if (!suggestedPath.get(0).src()
+ .deviceId().equals(intent.filteredIngressPoint().connectPoint().deviceId())) {
+ return false;
+ }
+ if (!suggestedPath.get(suggestedPath.size() - 1).dst()
+ .deviceId().equals(intent.filteredEgressPoint().connectPoint().deviceId())) {
+ return false;
+ }
+
+ // Check contiguity
+ List<Pair<Link, Link>> linkPairs = IntStream.
+ range(0, suggestedPath.size() - 1)
+ .mapToObj(i -> Pair.of(suggestedPath.get(i), suggestedPath.get(i + 1)))
+ .collect(Collectors.toList());
+
+ for (Pair<Link, Link> linkPair : linkPairs) {
+ if (!linkPair.getKey().dst().deviceId().equals(linkPair.getValue().src().deviceId())) {
+ return false;
+ }
+ }
+ return true;
+ }
}
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/PointToPointIntentCompilerTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/PointToPointIntentCompilerTest.java
index a811ad6..31c15ee 100644
--- a/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/PointToPointIntentCompilerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/PointToPointIntentCompilerTest.java
@@ -25,6 +25,7 @@
import org.onosproject.net.DeviceId;
import org.onosproject.net.FilteredConnectPoint;
import org.onosproject.net.Link;
+import org.onosproject.net.NetTestTools;
import org.onosproject.net.PortNumber;
import org.onosproject.net.ResourceGroup;
import org.onosproject.net.flow.TrafficSelector;
@@ -200,6 +201,54 @@
}
/**
+ * Creates a PointToPoint intent based on ingress and egress deviceIds
+ * and a suggested path.
+ * @param ingress the ingress connect point
+ * @param egress the egress connect point
+ * @param suggestedPath the suggested path
+ * @return the PointToPointIntent connecting the two connect points with
+ * the suggested path (if available)
+ */
+ private PointToPointIntent makeIntentSuggestedPath(ConnectPoint ingress,
+ ConnectPoint egress,
+ List<Link> suggestedPath) {
+ return PointToPointIntent.builder()
+ .appId(APPID)
+ .selector(selector)
+ .treatment(treatment)
+ .filteredIngressPoint(new FilteredConnectPoint(ingress))
+ .filteredEgressPoint(new FilteredConnectPoint(egress))
+ .suggestedPath(suggestedPath)
+ .build();
+ }
+
+ /**
+ * Creates a PointToPoint intent based on ingress and egress deviceIds and
+ * constraints with a suggested path.
+ *
+ * @param ingress the ingress connect point
+ * @param egress the egress connect point
+ * @param suggestedPath the suggested path
+ * @param constraints constraints
+ * @return the PointToPointIntent connecting the two connect points with
+ * constraints and a suggested path
+ */
+ private PointToPointIntent makeIntentSuggestedPath(ConnectPoint ingress,
+ ConnectPoint egress,
+ List<Link> suggestedPath,
+ List<Constraint> constraints) {
+ return PointToPointIntent.builder()
+ .appId(APPID)
+ .selector(selector)
+ .treatment(treatment)
+ .filteredIngressPoint(new FilteredConnectPoint(ingress))
+ .filteredEgressPoint(new FilteredConnectPoint(egress))
+ .constraints(constraints)
+ .suggestedPath(suggestedPath)
+ .build();
+ }
+
+ /**
* Creates a compiler for HostToHost intents.
*
* @param hops string array describing the path hops to use when compiling
@@ -210,6 +259,41 @@
}
/**
+ * Creates a compiler for PointToPoint intents with suggested paths.
+ *
+ * @param paths all the possible paths in the network
+ * @return PointToPoint intent compiler
+ */
+ private PointToPointIntentCompiler makeCompilerSuggestedPath(String[][] paths) {
+ final PointToPointIntentCompiler compiler = new PointToPointIntentCompiler();
+ compiler.pathService = new IntentTestsMocks.MockMultiplePathService(paths);
+ compiler.linkService = new IntentTestsMocks.MockLinkService(paths);
+ return compiler;
+ }
+
+ /**
+ * Creates a point to point intent compiler for suggested path case.
+ *
+ * @param paths all the possible paths in the network
+ * @param resourceService service to use for resource allocation requests
+ * @return point to point compiler
+ */
+ private PointToPointIntentCompiler makeCompilerSuggestedPath(String[][] paths,
+ ResourceService resourceService) {
+ final PointToPointIntentCompiler compiler = new PointToPointIntentCompiler();
+ compiler.pathService = new IntentTestsMocks.MockMultiplePathService(paths);
+ compiler.linkService = new IntentTestsMocks.MockLinkService(paths);
+
+ if (resourceService == null) {
+ compiler.resourceService = new MockResourceService();
+ } else {
+ compiler.resourceService = resourceService;
+ }
+
+ return compiler;
+ }
+
+ /**
* Creates a point to point intent compiler for a three switch linear
* topology.
*
@@ -582,4 +666,151 @@
assertThat(resourceAllocations, hasSize(6));
assertEquals(expectedresourceAllocations, resourceAllocations);
}
+
+ /**
+ * Test if a suggested path is correctly applied.
+ */
+ @Test
+ public void testSuggestedPath() {
+ String[] suggestedPathHops = {S1, S3, S4, S5, S6, S8};
+ List<Link> suggestedPath = NetTestTools.createPath(suggestedPathHops).links();
+
+ PointToPointIntent intent = makeIntentSuggestedPath(new ConnectPoint(DID_1, PORT_1),
+ new ConnectPoint(DID_8, PORT_2),
+ suggestedPath);
+
+ String[][] paths = {{S1, S2, S8}, suggestedPathHops};
+ PointToPointIntentCompiler compiler = makeCompilerSuggestedPath(paths);
+
+ List<Intent> result = compiler.compile(intent, null);
+ assertThat(result, is(Matchers.notNullValue()));
+ assertThat(result, hasSize(1));
+ Intent resultIntent = result.get(0);
+ assertThat(resultIntent instanceof LinkCollectionIntent, is(true));
+
+ if (resultIntent instanceof LinkCollectionIntent) {
+ LinkCollectionIntent resultLinkIntent = (LinkCollectionIntent) resultIntent;
+ FilteredConnectPoint ingressPoint = new FilteredConnectPoint(new ConnectPoint(DID_1, PORT_1));
+ FilteredConnectPoint egressPoint = new FilteredConnectPoint(new ConnectPoint(DID_8, PORT_2));
+ // 5 links for the hops, plus one default link on ingress and egress
+ assertThat(resultLinkIntent.links(), hasSize(suggestedPathHops.length - 1));
+ assertThat(resultLinkIntent.links(), linksHasPath(S1, S3));
+ assertThat(resultLinkIntent.links(), linksHasPath(S3, S4));
+ assertThat(resultLinkIntent.links(), linksHasPath(S4, S5));
+ assertThat(resultLinkIntent.links(), linksHasPath(S5, S6));
+ assertThat(resultLinkIntent.links(), linksHasPath(S6, S8));
+ assertThat(resultLinkIntent.filteredIngressPoints(), is(ImmutableSet.of(ingressPoint)));
+ assertThat(resultLinkIntent.filteredEgressPoints(), is(ImmutableSet.of(egressPoint)));
+ }
+ assertThat("key is inherited", resultIntent.key(), is(intent.key()));
+ }
+
+ /**
+ * Test that if a suggested path isn't available it applies another available path.
+ */
+ @Test
+ public void testSuggestedPathNotAvailable() {
+ String[] suggestedPathHops = {S1, S3, S8};
+ String[] shortestPath = {S1, S2, S8};
+ List<Link> suggestedPath = NetTestTools.createPath(suggestedPathHops).links();
+
+ PointToPointIntent intent = makeIntentSuggestedPath(new ConnectPoint(DID_1, PORT_1),
+ new ConnectPoint(DID_8, PORT_2),
+ suggestedPath);
+
+ String[][] path = {shortestPath};
+ PointToPointIntentCompiler compiler = makeCompilerSuggestedPath(path);
+
+ List<Intent> result = compiler.compile(intent, null);
+ assertThat(result, is(Matchers.notNullValue()));
+ assertThat(result, hasSize(1));
+ Intent resultIntent = result.get(0);
+ assertThat(resultIntent instanceof LinkCollectionIntent, is(true));
+
+ if (resultIntent instanceof LinkCollectionIntent) {
+ LinkCollectionIntent resultLinkIntent = (LinkCollectionIntent) resultIntent;
+ FilteredConnectPoint ingressPoint = new FilteredConnectPoint(new ConnectPoint(DID_1, PORT_1));
+ FilteredConnectPoint egressPoint = new FilteredConnectPoint(new ConnectPoint(DID_8, PORT_2));
+ // 5 links for the hops, plus one default link on ingress and egress
+ assertThat(resultLinkIntent.links(), hasSize(shortestPath.length - 1));
+ assertThat(resultLinkIntent.links(), linksHasPath(S1, S2));
+ assertThat(resultLinkIntent.links(), linksHasPath(S2, S8));
+ assertThat(resultLinkIntent.filteredIngressPoints(), is(ImmutableSet.of(ingressPoint)));
+ assertThat(resultLinkIntent.filteredEgressPoints(), is(ImmutableSet.of(egressPoint)));
+ }
+ assertThat("key is inherited", resultIntent.key(), is(intent.key()));
+ }
+
+ /**
+ * Tests that requests with suggested path
+ * and with sufficient available bandwidth succeed.
+ */
+ @Test
+ public void testSuggestedPathBandwidthConstrainedIntentSuccess() {
+ final double bpsTotal = 1000.0;
+ final double bpsToReserve = 100.0;
+
+ final ResourceService resourceService =
+ MockResourceService.makeCustomBandwidthResourceService(bpsTotal);
+ final List<Constraint> constraints =
+ Collections.singletonList(new BandwidthConstraint(Bandwidth.bps(bpsToReserve)));
+
+ String[] suggestedPathHops = {S1, S4, S5, S3};
+ List<Link> suggestedPath = NetTestTools.createPath(suggestedPathHops).links();
+
+ final PointToPointIntent intent = makeIntentSuggestedPath(
+ new ConnectPoint(DID_1, PORT_1),
+ new ConnectPoint(DID_3, PORT_2),
+ suggestedPath,
+ constraints);
+
+ String[][] hops = {{S1, S2, S3}, suggestedPathHops};
+ final PointToPointIntentCompiler compiler = makeCompilerSuggestedPath(hops,
+ resourceService);
+
+ final List<Intent> compiledIntents = compiler.compile(intent, null);
+
+ assertThat(compiledIntents, Matchers.notNullValue());
+ assertThat(compiledIntents, hasSize(1));
+
+ assertThat("key is inherited",
+ compiledIntents.stream().map(Intent::key).collect(Collectors.toList()),
+ everyItem(is(intent.key())));
+
+ }
+
+ /**
+ * Tests that requests with insufficient available bandwidth fail.
+ */
+ @Test
+ public void testSuggestedPathBandwidthConstrainedIntentFailure() {
+ final double bpsTotal = 10.0;
+
+ final ResourceService resourceService =
+ MockResourceService.makeCustomBandwidthResourceService(bpsTotal);
+ final List<Constraint> constraints =
+ Collections.singletonList(new BandwidthConstraint(Bandwidth.bps(BPS_TO_RESERVE)));
+
+ String[] suggestedPathHops = {S1, S4, S5, S3};
+ List<Link> suggestedPath = NetTestTools.createPath(suggestedPathHops).links();
+
+ try {
+ final PointToPointIntent intent = makeIntentSuggestedPath(
+ new ConnectPoint(DID_1, PORT_1),
+ new ConnectPoint(DID_3, PORT_2),
+ suggestedPath,
+ constraints);
+
+ String[][] paths = {{S1, S2, S3}, suggestedPathHops};
+ final PointToPointIntentCompiler compiler = makeCompilerSuggestedPath(paths,
+ resourceService);
+
+ compiler.compile(intent, null);
+
+ fail("Point to Point compilation with insufficient bandwidth does "
+ + "not throw exception.");
+ } catch (PathNotFoundException noPath) {
+ assertThat(noPath.getMessage(), containsString("No path"));
+ }
+ }
}