Use k-shortest paths in optical path provisioner (ONOS-6289).

Change-Id: Ida22317e674e1b30dc8b385e30ff347ad040b8c8
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) {