ONOS-5808: Allocate BW from ConnectivityIntentCompiler and unit tests for partial failure

Change-Id: I2eb3c16efbce619db6d0d2ba415a35752a61ece4
diff --git a/core/api/src/main/java/org/onosproject/net/intent/Key.java b/core/api/src/main/java/org/onosproject/net/intent/Key.java
index b4dab2d..e8e4af7 100644
--- a/core/api/src/main/java/org/onosproject/net/intent/Key.java
+++ b/core/api/src/main/java/org/onosproject/net/intent/Key.java
@@ -19,6 +19,8 @@
 import com.google.common.hash.HashFunction;
 import com.google.common.hash.Hashing;
 import org.onosproject.core.ApplicationId;
+import org.onosproject.net.resource.ResourceConsumer;
+import org.onosproject.net.resource.ResourceConsumerId;
 
 import java.nio.charset.StandardCharsets;
 import java.util.Objects;
@@ -28,7 +30,7 @@
  */
 // TODO maybe pull this up to utils
 @Beta
-public abstract class Key implements Comparable<Key> {
+public abstract class Key implements Comparable<Key>, ResourceConsumer {
 
     //TODO consider making this a HashCode object (worry about performance)
     private final long hash;
@@ -54,6 +56,8 @@
      * Creates a key based on the provided string.
      * <p>
      * Note: Two keys with equal value, but different appId, are not equal.
+     * Warning: it is caller responsibility to make sure the hashed value of
+     * {@code value} is unique.
      * </p>
      *
      * @param key the provided string
@@ -79,6 +83,11 @@
         return new LongKey(key, appId);
     }
 
+    @Override
+    public ResourceConsumerId consumerId() {
+        return ResourceConsumerId.of(hash(), getClass());
+    }
+
     private static final class StringKey extends Key {
 
         private final ApplicationId appId;
diff --git a/core/api/src/test/java/org/onosproject/net/NetTestTools.java b/core/api/src/test/java/org/onosproject/net/NetTestTools.java
index d6227a7..dd03a09 100644
--- a/core/api/src/test/java/org/onosproject/net/NetTestTools.java
+++ b/core/api/src/test/java/org/onosproject/net/NetTestTools.java
@@ -76,17 +76,27 @@
     }
 
     // Crates a new host with the specified id
-    public static Host host(String id, String did) {
+    public static Host host(String id, String did, long port) {
         return new DefaultHost(PID, hid(id), valueOf(1234), vlanId((short) 2),
-                               new HostLocation(did(did), portNumber(1), 321),
+                               new HostLocation(did(did), portNumber(port), 321),
                                new HashSet<>());
     }
 
+    // Crates a new host with the specified id
+    public static Host host(String id, String did) {
+        return host(id, did, 1);
+    }
+
     // Short-hand for creating a connection point.
     public static ConnectPoint connectPoint(String id, int port) {
         return new ConnectPoint(did(id), portNumber(port));
     }
 
+    // Short-hand for creating a connection point.
+    public static ConnectPoint connectPointNoOF(String id, int port) {
+        return new ConnectPoint(DeviceId.deviceId(id), portNumber(port));
+    }
+
     // Short-hand for creating a link.
     public static Link link(String src, int sp, String dst, int dp) {
         return new DefaultLink(PID,
@@ -95,6 +105,14 @@
                                Link.Type.DIRECT, Link.State.ACTIVE);
     }
 
+    // Short-hand for creating a link.
+    public static Link linkNoPrefixes(String src, int sp, String dst, int dp) {
+        return new DefaultLink(PID,
+                               connectPointNoOF(src, sp),
+                               connectPointNoOF(dst, dp),
+                               Link.Type.DIRECT, Link.State.ACTIVE);
+    }
+
     /**
      * Short-hand for creating a link.
      *
@@ -110,21 +128,21 @@
     public static Path createPath(String... ids) {
         List<Link> links = new ArrayList<>();
         for (int i = 0; i < ids.length - 1; i++) {
-            links.add(link(ids[i], i, ids[i + 1], i));
+            links.add(link(ids[i], 2, ids[i + 1], 1));
         }
         return new DefaultPath(PID, links, ids.length);
     }
 
-    // Creates a path that leads through the given devices.
+    // Creates a path that leads through the given hosts.
     public static Path createPath(boolean srcIsEdge, boolean dstIsEdge, String... ids) {
         List<Link> links = new ArrayList<>();
         for (int i = 0; i < ids.length - 1; i++) {
             if (i == 0 && srcIsEdge) {
-                links.add(DefaultEdgeLink.createEdgeLink(host(ids[i], ids[i + 1]), true));
+                links.add(DefaultEdgeLink.createEdgeLink(host(ids[i], ids[i + 1], 1), true));
             } else if (i == ids.length - 2 && dstIsEdge) {
-                links.add(DefaultEdgeLink.createEdgeLink(host(ids[i + 1], ids[i]), false));
+                links.add(DefaultEdgeLink.createEdgeLink(host(ids[i + 1], ids[i], 2), false));
             } else {
-                links.add(link(ids[i], i, ids[i + 1], i));
+                links.add(link(ids[i], 2, ids[i + 1], 1));
             }
         }
         return new DefaultPath(PID, links, ids.length);
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 a944ca6..cef5b67 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
@@ -17,9 +17,9 @@
 
 import com.google.common.base.MoreObjects;
 import org.onlab.graph.Weight;
-import org.onlab.util.Bandwidth;
 import org.onosproject.core.DefaultGroupId;
 import org.onosproject.core.GroupId;
+import org.onosproject.net.DefaultPath;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.ElementId;
 import org.onosproject.net.HostId;
@@ -38,13 +38,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.resource.DiscreteResourceId;
-import org.onosproject.net.resource.Resource;
-import org.onosproject.net.resource.ResourceAllocation;
-import org.onosproject.net.resource.ResourceConsumer;
-import org.onosproject.net.resource.ResourceId;
-import org.onosproject.net.resource.ResourceListener;
-import org.onosproject.net.resource.ResourceService;
+import org.onosproject.net.provider.ProviderId;
 import org.onosproject.net.topology.DefaultTopologyEdge;
 import org.onosproject.net.topology.DefaultTopologyVertex;
 import org.onosproject.net.topology.LinkWeigher;
@@ -52,13 +46,13 @@
 import org.onosproject.net.topology.TopologyVertex;
 import org.onosproject.store.Timestamp;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
-import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicLong;
 
@@ -185,8 +179,7 @@
      * Mock path service for creating paths within the test.
      *
      */
-    public static class Mp2MpMockPathService
-            extends PathServiceAdapter {
+    public static class Mp2MpMockPathService extends PathServiceAdapter {
 
         final String[] pathHops;
         final String[] reversePathHops;
@@ -242,84 +235,74 @@
         }
     }
 
-    public static final class MockResourceService implements ResourceService {
+    /**
+     * Mock path service for creating paths for MP2SP intent tests, returning
+     * pre-determined paths.
+     */
+    public static class FixedMP2MPMockPathService extends PathServiceAdapter {
 
-        private final double bandwidth;
+        final String[] pathHops;
 
-        public static ResourceService makeBandwidthResourceService(double bandwidth) {
-            return new MockResourceService(bandwidth);
-        }
+        public static final String DPID_1 = "of:s1";
+        public static final String DPID_2 = "of:s2";
+        public static final String DPID_3 = "of:s3";
+        public static final String DPID_4 = "of:s4";
 
-        private MockResourceService(double bandwidth) {
-            this.bandwidth = bandwidth;
+        /**
+         * Constructor that provides a set of hops to mock.
+         *
+         * @param pathHops path hops to mock
+         */
+        public FixedMP2MPMockPathService(String[] pathHops) {
+            this.pathHops = pathHops;
         }
 
         @Override
-        public List<ResourceAllocation> allocate(ResourceConsumer consumer, List<? extends Resource> resources) {
-            return null;
-        }
-
-        @Override
-        public boolean release(List<ResourceAllocation> allocations) {
-            return false;
-        }
-
-        @Override
-        public boolean release(ResourceConsumer consumer) {
-            return false;
-        }
-
-        @Override
-        public List<ResourceAllocation> getResourceAllocations(ResourceId id) {
-            return null;
-        }
-
-        @Override
-        public <T> Collection<ResourceAllocation> getResourceAllocations(DiscreteResourceId parent, Class<T> cls) {
-            return null;
-        }
-
-        @Override
-        public Collection<ResourceAllocation> getResourceAllocations(ResourceConsumer consumer) {
-            return null;
-        }
-
-        @Override
-        public Set<Resource> getAvailableResources(DiscreteResourceId parent) {
-            return null;
-        }
-
-        @Override
-        public <T> Set<Resource> getAvailableResources(DiscreteResourceId parent, Class<T> cls) {
-            return null;
-        }
-
-        @Override
-        public <T> Set<T> getAvailableResourceValues(DiscreteResourceId parent, Class<T> cls) {
-            return null;
-        }
-
-        @Override
-        public Set<Resource> getRegisteredResources(DiscreteResourceId parent) {
-            return null;
-        }
-
-        @Override
-        public boolean isAvailable(Resource resource) {
-            if (!resource.isTypeOf(Bandwidth.class)) {
-                return false;
+        public Set<Path> getPaths(ElementId src, ElementId dst) {
+            List<Link> links = new ArrayList<>();
+            Set<Path> result = new HashSet<>();
+            ProviderId providerId = new ProviderId("of", "foo");
+            DefaultPath path;
+            if (src.toString().equals(DPID_1) && dst.toString().equals(DPID_4)) {
+                links.add(NetTestTools.linkNoPrefixes(src.toString(), 2, pathHops[0], 1));
+                links.add(NetTestTools.linkNoPrefixes(pathHops[0], 2, dst.toString(), 1));
+            } else if (src.toString().equals(DPID_2) && dst.toString().equals(DPID_4)) {
+                links.add(NetTestTools.linkNoPrefixes(src.toString(), 2, pathHops[0], 3));
+                links.add(NetTestTools.linkNoPrefixes(pathHops[0], 2, dst.toString(), 1));
+            } else if (src.toString().equals(DPID_4) && dst.toString().equals(DPID_1)) {
+                links.add(NetTestTools.linkNoPrefixes(src.toString(), 2, pathHops[0], 1));
+                links.add(NetTestTools.linkNoPrefixes(pathHops[0], 2, dst.toString(), 1));
+            } else if (src.toString().equals(DPID_4) && dst.toString().equals(DPID_2)) {
+                links.add(NetTestTools.linkNoPrefixes(src.toString(), 2, pathHops[0], 1));
+                links.add(NetTestTools.linkNoPrefixes(pathHops[0], 3, dst.toString(), 1));
+            } else {
+                return result;
             }
+            path = new DefaultPath(providerId, links, 3);
+            result.add(path);
 
-            Optional<Double> value = resource.valueAs(Double.class);
-            return value.filter(requested -> requested <= bandwidth).isPresent();
+            return result;
         }
 
         @Override
-        public void addListener(ResourceListener listener) {
-        }
+        public Set<Path> getPaths(ElementId src, ElementId dst, LinkWeigher weigher) {
+            final Set<Path> paths = getPaths(src, dst);
 
-        @Override
-        public void removeListener(ResourceListener listener) {
+            for (Path path : paths) {
+                final DeviceId srcDevice = path.src().elementId() instanceof DeviceId ? path.src().deviceId() : null;
+                final DeviceId dstDevice = path.dst().elementId() instanceof DeviceId ? path.dst().deviceId() : null;
+                if (srcDevice != null && dstDevice != null) {
+                    final TopologyVertex srcVertex = new DefaultTopologyVertex(srcDevice);
+                    final TopologyVertex dstVertex = new DefaultTopologyVertex(dstDevice);
+                    final Link link = link(src.toString(), 1, dst.toString(), 1);
+
+                    final Weight weightValue = weigher.weight(new DefaultTopologyEdge(srcVertex, dstVertex, link));
+                    if (weightValue.isNegative()) {
+                        return new HashSet<>();
+                    }
+                }
+            }
+            return paths;
         }
     }
 
@@ -500,5 +483,4 @@
             return true;
         }
     }
-
 }
diff --git a/core/api/src/test/java/org/onosproject/net/resource/MockResourceService.java b/core/api/src/test/java/org/onosproject/net/resource/MockResourceService.java
index 436a763..3a8b4e4 100644
--- a/core/api/src/test/java/org/onosproject/net/resource/MockResourceService.java
+++ b/core/api/src/test/java/org/onosproject/net/resource/MockResourceService.java
@@ -20,6 +20,7 @@
 
 import org.onlab.packet.MplsLabel;
 import org.onlab.packet.VlanId;
+import org.onlab.util.Bandwidth;
 import org.onlab.util.Tools;
 import org.onosproject.net.TributarySlot;
 
@@ -35,10 +36,22 @@
 
 public class MockResourceService implements ResourceService {
 
+    private double bandwidth = 1000.0;
     private final Map<Resource, ResourceConsumer> assignment = new HashMap<>();
     public Set<Short> availableVlanLabels = new HashSet<>();
     public Set<Integer> availableMplsLabels = new HashSet<>();
 
+    public MockResourceService(){}
+
+    // To express a custom bandwidth available (in bps)
+    public static ResourceService makeCustomBandwidthResourceService(double bandwidth) {
+        return new MockResourceService(bandwidth);
+    }
+
+    private MockResourceService(double bandwidth) {
+        this.bandwidth = bandwidth;
+    }
+
     @Override
     public List<ResourceAllocation> allocate(ResourceConsumer consumer, List<? extends Resource> resources) {
         assignment.putAll(
@@ -181,6 +194,11 @@
 
     @Override
     public boolean isAvailable(Resource resource) {
+        if (resource.isTypeOf(Bandwidth.class)) {
+            // If there's is enough bandwidth available return true; false otherwise
+            Optional<Double> value = resource.valueAs(Double.class);
+            return value.filter(requested -> requested <= bandwidth).isPresent();
+        }
         return true;
     }