Use k-shortest paths in optical path provisioner (ONOS-6289).
Change-Id: Ida22317e674e1b30dc8b385e30ff347ad040b8c8
diff --git a/apps/newoptical/BUCK b/apps/newoptical/BUCK
index ac24ee1..29f0fa9 100644
--- a/apps/newoptical/BUCK
+++ b/apps/newoptical/BUCK
@@ -8,6 +8,7 @@
TEST_DEPS = [
'//lib:TEST_ADAPTERS',
+ '//utils/osgi:onlab-osgi-tests',
]
osgi_jar_with_tests (
diff --git a/apps/newoptical/pom.xml b/apps/newoptical/pom.xml
index daab122..c1b97df 100644
--- a/apps/newoptical/pom.xml
+++ b/apps/newoptical/pom.xml
@@ -114,6 +114,13 @@
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onlab-osgi</artifactId>
+ <classifier>tests</classifier>
+ <scope>test</scope>
+ </dependency>
+
</dependencies>
<build>
diff --git a/apps/newoptical/src/main/java/org/onosproject/newoptical/OpticalPathProvisioner.java b/apps/newoptical/src/main/java/org/onosproject/newoptical/OpticalPathProvisioner.java
index 79d1d26..245a90f 100644
--- a/apps/newoptical/src/main/java/org/onosproject/newoptical/OpticalPathProvisioner.java
+++ b/apps/newoptical/src/main/java/org/onosproject/newoptical/OpticalPathProvisioner.java
@@ -21,23 +21,23 @@
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Modified;
+import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
+import org.onlab.graph.DefaultEdgeWeigher;
+import org.onlab.graph.ScalarWeight;
+import org.onlab.graph.Weight;
import org.onlab.util.Bandwidth;
import org.onlab.util.GuavaCollectors;
import org.onlab.util.KryoNamespace;
+import org.onlab.util.Tools;
import org.onosproject.cluster.ClusterService;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
-import org.onosproject.event.ListenerTracker;
-import org.onosproject.net.optical.OchPort;
-import org.onosproject.net.optical.OduCltPort;
-import org.onosproject.newoptical.api.OpticalConnectivityId;
-import org.onosproject.newoptical.api.OpticalPathEvent;
-import org.onosproject.newoptical.api.OpticalPathListener;
-import org.onosproject.newoptical.api.OpticalPathService;
import org.onosproject.event.AbstractListenerManager;
+import org.onosproject.event.ListenerTracker;
import org.onosproject.mastership.MastershipService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.Device;
@@ -47,6 +47,8 @@
import org.onosproject.net.Path;
import org.onosproject.net.Port;
import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.config.basics.BandwidthCapacity;
+import org.onosproject.net.config.basics.BasicLinkConfig;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.intent.Intent;
import org.onosproject.net.intent.IntentEvent;
@@ -58,16 +60,21 @@
import org.onosproject.net.link.LinkEvent;
import org.onosproject.net.link.LinkListener;
import org.onosproject.net.link.LinkService;
-import org.onosproject.net.config.basics.BandwidthCapacity;
-import org.onosproject.net.config.basics.BasicLinkConfig;
+import org.onosproject.net.optical.OchPort;
+import org.onosproject.net.optical.OduCltPort;
import org.onosproject.net.resource.ContinuousResource;
import org.onosproject.net.resource.Resource;
import org.onosproject.net.resource.ResourceAllocation;
import org.onosproject.net.resource.ResourceService;
import org.onosproject.net.resource.Resources;
-import org.onosproject.net.topology.LinkWeight;
-import org.onosproject.net.topology.PathService;
+import org.onosproject.net.topology.LinkWeigher;
import org.onosproject.net.topology.TopologyEdge;
+import org.onosproject.net.topology.TopologyService;
+import org.onosproject.net.topology.TopologyVertex;
+import org.onosproject.newoptical.api.OpticalConnectivityId;
+import org.onosproject.newoptical.api.OpticalPathEvent;
+import org.onosproject.newoptical.api.OpticalPathListener;
+import org.onosproject.newoptical.api.OpticalPathService;
import org.onosproject.store.serializers.KryoNamespaces;
import org.onosproject.store.service.AtomicCounter;
import org.onosproject.store.service.ConsistentMap;
@@ -77,6 +84,7 @@
import org.onosproject.store.service.Serializer;
import org.onosproject.store.service.StorageService;
import org.onosproject.store.service.Versioned;
+import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -84,6 +92,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.Dictionary;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -91,6 +100,7 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@@ -127,7 +137,7 @@
protected IntentService intentService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected PathService pathService;
+ protected TopologyService topologyService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
@@ -153,6 +163,11 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ResourceService resourceService;
+ private static final String MAX_PATHS = "maxPaths";
+ private static final int DEFAULT_MAX_PATHS = 10;
+ @Property(name = MAX_PATHS, intValue = DEFAULT_MAX_PATHS,
+ label = "Maximum number of paths to consider for path provisioning")
+ private int maxPaths = DEFAULT_MAX_PATHS;
private ApplicationId appId;
@@ -192,7 +207,7 @@
.register(KryoNamespaces.API);
@Activate
- protected void activate() {
+ protected void activate(ComponentContext context) {
deviceService = opticalView(deviceService);
appId = coreService.registerApplication("org.onosproject.newoptical");
@@ -225,6 +240,8 @@
linkPathMap.addListener(storeListener);
+ readComponentConfiguration(context);
+
log.info("Started");
}
@@ -237,6 +254,22 @@
log.info("Stopped");
}
+ @Modified
+ public void modified(ComponentContext context) {
+ readComponentConfiguration(context);
+ }
+
+ /**
+ * Extracts properties from the component configuration context.
+ *
+ * @param context the component context
+ */
+ private void readComponentConfiguration(ComponentContext context) {
+ Dictionary<?, ?> properties = context.getProperties();
+ maxPaths = Tools.getIntegerProperty(properties, MAX_PATHS, DEFAULT_MAX_PATHS);
+ log.info("Configured. Maximum paths to consider is configured to {}", maxPaths);
+ }
+
@Override
public Collection<OpticalConnectivity> listConnectivity() {
return connectivityMap.values().stream()
@@ -270,32 +303,28 @@
checkNotNull(egress);
log.info("setupConnectivity({}, {}, {}, {})", ingress, egress, bandwidth, latency);
- bandwidth = (bandwidth == null) ? NO_BW_REQUIREMENT : bandwidth;
+ Bandwidth bw = (bandwidth == null) ? NO_BW_REQUIREMENT : bandwidth;
- Set<Path> paths = pathService.getPaths(ingress.deviceId(), egress.deviceId(),
+ Stream<Path> paths = topologyService.getKShortestPaths(
+ topologyService.currentTopology(),
+ ingress.deviceId(), egress.deviceId(),
new BandwidthLinkWeight(bandwidth));
- if (paths.isEmpty()) {
- log.warn("Unable to find multi-layer path.");
- return null;
+
+ // Path service calculates from node to node, we're only interested in port to port
+ Optional<OpticalConnectivityId> id =
+ paths.filter(p -> p.src().equals(ingress) && p.dst().equals(egress))
+ .limit(maxPaths)
+ .map(p -> setupPath(p, bw, latency))
+ .filter(Objects::nonNull)
+ .findFirst();
+
+ if (id.isPresent()) {
+ log.info("Assigned OpticalConnectivityId: {}", id);
+ } else {
+ log.error("setupConnectivity({}, {}, {}, {}) failed.", ingress, egress, bandwidth, latency);
}
- // Search path with available cross connect points
- for (Path path : paths) {
- // Path service calculates from node to node, we're only interested in port to port
- if (!path.src().equals(ingress) || !path.dst().equals(egress)) {
- continue;
- }
-
- OpticalConnectivityId id = setupPath(path, bandwidth, latency);
- if (id != null) {
- log.info("Assigned OpticalConnectivityId: {}", id);
- return id;
- }
- }
-
- log.error("setupConnectivity({}, {}, {}, {}) failed.", ingress, egress, bandwidth, latency);
-
- return null;
+ return id.orElse(null);
}
/*
@@ -364,7 +393,6 @@
return null;
}
-
// create set of PacketLinkRealizedByOptical
Set<PacketLinkRealizedByOptical> packetLinks = createPacketLinkSet(crossConnectPoints,
intents, crossConnectPointMap);
@@ -647,7 +675,7 @@
return linkDiscoveryEnabled(cp1) && linkDiscoveryEnabled(cp2);
}
- private class BandwidthLinkWeight implements LinkWeight {
+ private class BandwidthLinkWeight extends DefaultEdgeWeigher<TopologyVertex, TopologyEdge> implements LinkWeigher {
private Bandwidth bandwidth = null;
public BandwidthLinkWeight(Bandwidth bandwidth) {
@@ -655,42 +683,38 @@
}
@Override
- public double weight(TopologyEdge edge) {
+ public Weight weight(TopologyEdge edge) {
Link l = edge.link();
// Avoid inactive links
if (l.state() == Link.State.INACTIVE) {
log.trace("{} is not active", l);
- return -1.0;
+ return ScalarWeight.NON_VIABLE_WEIGHT;
}
// Avoid cross connect links with used ports
if (isCrossConnectLink(l) && usedCrossConnectLinkSet.contains(l)) {
log.trace("Cross connect {} in use", l);
- return -1.0;
+ return ScalarWeight.NON_VIABLE_WEIGHT;
}
// Check availability of bandwidth
if (bandwidth != null && !NO_BW_REQUIREMENT.equals(bandwidth)) {
if (hasEnoughBandwidth(l.src()) && hasEnoughBandwidth(l.dst())) {
- return 1.0;
+ return new ScalarWeight(1.0);
} else {
log.trace("Not enough bandwidth on {}", l);
- return -1.0;
+ return ScalarWeight.NON_VIABLE_WEIGHT;
}
+ // Allow everything else
} else {
- // Use everything except our own indirect links
- if (l.type() == Link.Type.INDIRECT) {
- return -1.0;
- } else {
- return 1.0;
- }
+ return new ScalarWeight(1.0);
}
}
private boolean hasEnoughBandwidth(ConnectPoint cp) {
if (cp.elementId() instanceof DeviceId) {
- Device device = deviceService.getDevice(cp.deviceId());
+ Device device = deviceService.getDevice(cp.deviceId());
Device.Type type = device.type();
if (isTransportLayer(type)) {
@@ -711,7 +735,7 @@
return resourceService.isAvailable(resource);
} catch (Exception e) {
log.error("Resource service failed checking availability of {}",
- resource, e);
+ resource, e);
throw e;
}
}
@@ -720,7 +744,6 @@
}
}
-
public class InternalIntentListener implements IntentListener {
@Override
public void event(IntentEvent event) {
diff --git a/apps/newoptical/src/main/java/org/onosproject/newoptical/api/OpticalPathService.java b/apps/newoptical/src/main/java/org/onosproject/newoptical/api/OpticalPathService.java
index 15837c7..5092d67 100644
--- a/apps/newoptical/src/main/java/org/onosproject/newoptical/api/OpticalPathService.java
+++ b/apps/newoptical/src/main/java/org/onosproject/newoptical/api/OpticalPathService.java
@@ -43,7 +43,7 @@
* @param egress egress port
* @param bandwidth required bandwidth. No bandwidth is assured if null.
* @param latency required latency. No latency is assured if null.
- * @return ID of created connectivity if successful. null otherwise.
+ * @return id of created connectivity if successful, null otherwise.
*/
OpticalConnectivityId setupConnectivity(ConnectPoint ingress, ConnectPoint egress,
Bandwidth bandwidth, Duration latency);
@@ -54,7 +54,7 @@
* @param path multi-layer path along which connectivity will be set up
* @param bandwidth required bandwidth. No bandwidth is assured if null.
* @param latency required latency. No latency is assured if null.
- * @return true if successful. false otherwise.
+ * @return id of created connectivity if successful, null otherwise.
*/
OpticalConnectivityId setupPath(Path path, Bandwidth bandwidth, Duration latency);
diff --git a/apps/newoptical/src/test/java/org/onosproject/newoptical/OpticalPathProvisionerTest.java b/apps/newoptical/src/test/java/org/onosproject/newoptical/OpticalPathProvisionerTest.java
index 78a58b5..8a98864 100644
--- a/apps/newoptical/src/test/java/org/onosproject/newoptical/OpticalPathProvisionerTest.java
+++ b/apps/newoptical/src/test/java/org/onosproject/newoptical/OpticalPathProvisionerTest.java
@@ -20,6 +20,8 @@
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import org.onlab.graph.ScalarWeight;
+import org.onlab.osgi.ComponentContextAdapter;
import org.onlab.packet.ChassisId;
import org.onlab.packet.IpAddress;
import org.onlab.util.Bandwidth;
@@ -45,7 +47,6 @@
import org.onosproject.net.DefaultPort;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
-import org.onosproject.net.ElementId;
import org.onosproject.net.Link;
import org.onosproject.net.MastershipRole;
import org.onosproject.net.OchSignal;
@@ -75,8 +76,9 @@
import org.onosproject.net.resource.ResourceId;
import org.onosproject.net.resource.ResourceListener;
import org.onosproject.net.resource.ResourceService;
-import org.onosproject.net.topology.LinkWeight;
-import org.onosproject.net.topology.PathServiceAdapter;
+import org.onosproject.net.topology.LinkWeigher;
+import org.onosproject.net.topology.Topology;
+import org.onosproject.net.topology.TopologyServiceAdapter;
import org.onosproject.newoptical.api.OpticalConnectivityId;
import org.onosproject.newoptical.api.OpticalPathEvent;
import org.onosproject.newoptical.api.OpticalPathListener;
@@ -98,7 +100,6 @@
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -173,7 +174,7 @@
protected TestListener listener = new TestListener();
protected TestDeviceService deviceService;
protected TestLinkService linkService;
- protected TestPathService pathService;
+ protected TestTopologyService topologyService;
protected TestIntentService intentService;
protected TestMastershipService mastershipService;
protected TestClusterService clusterService;
@@ -208,7 +209,7 @@
linkService.links.addAll(Stream.of(LINK1, LINK2, LINK3, LINK4, LINK5, LINK6)
.collect(Collectors.toList()));
- this.pathService = new TestPathService();
+ this.topologyService = new TestTopologyService();
this.intentService = new TestIntentService();
this.mastershipService = new TestMastershipService();
this.clusterService = new TestClusterService();
@@ -224,7 +225,7 @@
this.target = new OpticalPathProvisioner();
target.coreService = new TestCoreService();
target.intentService = this.intentService;
- target.pathService = this.pathService;
+ target.topologyService = this.topologyService;
target.linkService = this.linkService;
target.mastershipService = this.mastershipService;
target.clusterService = this.clusterService;
@@ -235,7 +236,7 @@
injectEventDispatcher(target, new TestEventDispatcher());
target.addListener(listener);
- target.activate();
+ target.activate(new ComponentContextAdapter());
// To overwrite opticalView-ed deviceService
target.deviceService = this.deviceService;
@@ -271,9 +272,9 @@
assertNotNull(cid);
// Checks path computation is called as expected
- assertEquals(1, pathService.edges.size());
- assertEquals(CP12.deviceId(), pathService.edges.get(0).getKey());
- assertEquals(CP71.deviceId(), pathService.edges.get(0).getValue());
+ assertEquals(1, topologyService.edges.size());
+ assertEquals(CP12.deviceId(), topologyService.edges.get(0).getKey());
+ assertEquals(CP71.deviceId(), topologyService.edges.get(0).getValue());
// Checks intents are installed as expected
assertEquals(1, intentService.submitted.size());
@@ -292,7 +293,7 @@
Duration latency = Duration.ofMillis(10);
List<Link> links = Stream.of(LINK1, LINK2, LINK3, LINK4, LINK5, LINK6)
.collect(Collectors.toList());
- Path path = new DefaultPath(PROVIDER_ID, links, 0);
+ Path path = new DefaultPath(PROVIDER_ID, links, new ScalarWeight(0));
OpticalConnectivityId cid = target.setupPath(path, bandwidth, latency);
assertNotNull(cid);
@@ -494,26 +495,25 @@
}
}
- private static class TestPathService extends PathServiceAdapter {
+ private static class TestTopologyService extends TopologyServiceAdapter {
List<Pair<DeviceId, DeviceId>> edges = new ArrayList<>();
@Override
- public Set<Path> getPaths(ElementId src, ElementId dst, LinkWeight weight) {
+ public Stream<Path> getKShortestPaths(Topology topology, DeviceId src, DeviceId dst, LinkWeigher weigher) {
if (!(src instanceof DeviceId && dst instanceof DeviceId)) {
- return Collections.emptySet();
+ return Stream.empty();
}
- edges.add(Pair.of((DeviceId) src, (DeviceId) dst));
+ edges.add(Pair.of(src, dst));
Set<Path> paths = new HashSet<>();
List<Link> links = Stream.of(LINK1, LINK2, LINK3, LINK4, LINK5, LINK6)
.collect(Collectors.toList());
- paths.add(new DefaultPath(PROVIDER_ID, links, 0));
+ paths.add(new DefaultPath(PROVIDER_ID, links, new ScalarWeight(0)));
// returns paths containing single path
- return paths;
+ return paths.stream();
}
-
}
private static class TestIntentService extends IntentServiceAdapter {
@@ -712,7 +712,6 @@
return newValue;
}
-
@Override
public Set<Map.Entry<K, Versioned<V>>> entrySet() {
return map.entrySet();
@@ -765,7 +764,6 @@
return null;
}
}
-
}
private static class TestDeviceService extends DeviceServiceAdapter {
diff --git a/core/api/src/test/java/org/onosproject/net/topology/TopologyServiceAdapter.java b/core/api/src/test/java/org/onosproject/net/topology/TopologyServiceAdapter.java
index 79ec17c..bbc8fde 100644
--- a/core/api/src/test/java/org/onosproject/net/topology/TopologyServiceAdapter.java
+++ b/core/api/src/test/java/org/onosproject/net/topology/TopologyServiceAdapter.java
@@ -23,6 +23,7 @@
import java.util.Map;
import java.util.Set;
+import java.util.stream.Stream;
/**
* Test adapter for topology service.
@@ -83,6 +84,17 @@
}
@Override
+ public Set<Path> getKShortestPaths(Topology topology, DeviceId src, DeviceId dst,
+ LinkWeigher weigher, int maxPaths) {
+ return null;
+ }
+
+ @Override
+ public Stream<Path> getKShortestPaths(Topology topology, DeviceId src, DeviceId dst, LinkWeigher weigher) {
+ return null;
+ }
+
+ @Override
public boolean isInfrastructure(Topology topology,
ConnectPoint connectPoint) {
return false;