Prototype bandwidth reservation

This is a prototype of the bandwidth reservation implementation.
There is no bandwidth discovery, it is all faked.  The bandwidth is
specified by allocating a special Intent used for demonstration purposes.
This code also uses faked out interfaces in the Resource Manager, and will
need to be refactored when the real Resource Manager is in place.

Change-Id: I1f9a16b4144f5440bb529014a6a6f0f21d22839e
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/HostToHostIntentCompiler.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/HostToHostIntentCompiler.java
index e045cec..37cf84b 100644
--- a/core/net/src/main/java/org/onlab/onos/net/intent/impl/HostToHostIntentCompiler.java
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/HostToHostIntentCompiler.java
@@ -15,6 +15,10 @@
  */
 package org.onlab.onos.net.intent.impl;
 
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -32,13 +36,10 @@
 import org.onlab.onos.net.intent.IntentExtensionService;
 import org.onlab.onos.net.intent.PathIntent;
 import org.onlab.onos.net.topology.LinkWeight;
+import org.onlab.onos.net.resource.LinkResourceRequest;
 import org.onlab.onos.net.topology.PathService;
 import org.onlab.onos.net.topology.TopologyEdge;
 
-import java.util.Arrays;
-import java.util.List;
-import java.util.Set;
-
 import static org.onlab.onos.net.flow.DefaultTrafficSelector.builder;
 
 /**
@@ -85,7 +86,7 @@
         TrafficSelector selector = builder(intent.selector())
                 .matchEthSrc(src.mac()).matchEthDst(dst.mac()).build();
         return new PathIntent(intent.appId(), selector, intent.treatment(),
-                              path);
+                              path, new LinkResourceRequest[0]);
     }
 
     private Path getPath(HostId one, HostId two) {
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathIntentInstaller.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathIntentInstaller.java
index d0666cc..ad8ea08 100644
--- a/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathIntentInstaller.java
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathIntentInstaller.java
@@ -15,9 +15,6 @@
  */
 package org.onlab.onos.net.intent.impl;
 
-import static org.onlab.onos.net.flow.DefaultTrafficTreatment.builder;
-import static org.slf4j.LoggerFactory.getLogger;
-
 import java.util.Iterator;
 import java.util.List;
 
@@ -41,10 +38,15 @@
 import org.onlab.onos.net.intent.IntentExtensionService;
 import org.onlab.onos.net.intent.IntentInstaller;
 import org.onlab.onos.net.intent.PathIntent;
+import org.onlab.onos.net.resource.LinkResourceAllocations;
+import org.onlab.onos.net.resource.LinkResourceService;
 import org.slf4j.Logger;
 
 import com.google.common.collect.Lists;
 
+import static org.onlab.onos.net.flow.DefaultTrafficTreatment.builder;
+import static org.slf4j.LoggerFactory.getLogger;
+
 /**
  * Installer for {@link PathIntent packet path connectivity intents}.
  */
@@ -59,6 +61,9 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CoreService coreService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected LinkResourceService resourceService;
+
     private ApplicationId appId;
 
     @Activate
@@ -74,6 +79,11 @@
 
     @Override
     public List<FlowRuleBatchOperation> install(PathIntent intent) {
+        LinkResourceAllocations allocations = allocateBandwidth(intent);
+        if (allocations == null) {
+            log.debug("Insufficient bandwidth available to install path intent {}", intent);
+            return null;
+        }
         TrafficSelector.Builder builder =
                 DefaultTrafficSelector.builder(intent.selector());
         Iterator<Link> links = intent.path().links().iterator();
@@ -117,6 +127,10 @@
         return Lists.newArrayList(new FlowRuleBatchOperation(rules));
     }
 
+    private LinkResourceAllocations allocateBandwidth(PathIntent intent) {
+        return resourceService.requestResources(intent.resourceRequests()[0]);
+    }
+
     // TODO refactor below this line... ----------------------------
 
     /**
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/PointToPointIntentCompiler.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/PointToPointIntentCompiler.java
index 5c79d5c..893b7ea 100644
--- a/core/net/src/main/java/org/onlab/onos/net/intent/impl/PointToPointIntentCompiler.java
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/PointToPointIntentCompiler.java
@@ -15,6 +15,10 @@
  */
 package org.onlab.onos.net.intent.impl;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -31,15 +35,12 @@
 import org.onlab.onos.net.intent.PathIntent;
 import org.onlab.onos.net.intent.PointToPointIntent;
 import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.onos.net.resource.LinkResourceRequest;
 import org.onlab.onos.net.topology.LinkWeight;
 import org.onlab.onos.net.topology.Topology;
 import org.onlab.onos.net.topology.TopologyEdge;
 import org.onlab.onos.net.topology.TopologyService;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-
 import static java.util.Arrays.asList;
 
 /**
@@ -76,7 +77,7 @@
         links.add(DefaultEdgeLink.createEdgeLink(intent.egressPoint(), false));
 
         return asList(createPathIntent(new DefaultPath(PID, links, path.cost() + 2,
-                                                       path.annotations()), intent));
+                path.annotations()), intent));
     }
 
     /**
@@ -89,7 +90,8 @@
     private Intent createPathIntent(Path path,
                                     PointToPointIntent intent) {
         return new PathIntent(intent.appId(),
-                              intent.selector(), intent.treatment(), path);
+                              intent.selector(), intent.treatment(), path,
+                              new LinkResourceRequest[0]);
     }
 
     /**
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/PointToPointIntentWithBandwidthConstraintCompiler.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/PointToPointIntentWithBandwidthConstraintCompiler.java
new file mode 100644
index 0000000..a192a9f
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/PointToPointIntentWithBandwidthConstraintCompiler.java
@@ -0,0 +1,146 @@
+package org.onlab.onos.net.intent.impl;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+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.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.DefaultEdgeLink;
+import org.onlab.onos.net.DefaultPath;
+import org.onlab.onos.net.Link;
+import org.onlab.onos.net.Path;
+import org.onlab.onos.net.intent.Intent;
+import org.onlab.onos.net.intent.IntentCompiler;
+import org.onlab.onos.net.intent.IntentExtensionService;
+import org.onlab.onos.net.intent.PathIntent;
+import org.onlab.onos.net.intent.PointToPointIntent;
+import org.onlab.onos.net.intent.PointToPointIntentWithBandwidthConstraint;
+import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.onos.net.resource.BandwidthResourceRequest;
+import org.onlab.onos.net.resource.DefaultLinkResourceRequest;
+import org.onlab.onos.net.resource.LinkResourceRequest;
+import org.onlab.onos.net.resource.LinkResourceService;
+import org.onlab.onos.net.resource.ResourceRequest;
+import org.onlab.onos.net.resource.ResourceType;
+import org.onlab.onos.net.topology.LinkWeight;
+import org.onlab.onos.net.topology.Topology;
+import org.onlab.onos.net.topology.TopologyEdge;
+import org.onlab.onos.net.topology.TopologyService;
+
+/**
+ * A intent compiler for {@link org.onlab.onos.net.intent.HostToHostIntent}.
+ */
+@Component(immediate = true)
+public class PointToPointIntentWithBandwidthConstraintCompiler
+        implements IntentCompiler<PointToPointIntentWithBandwidthConstraint> {
+
+    private static final ProviderId PID = new ProviderId("core", "org.onlab.onos.core", true);
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected IntentExtensionService intentManager;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected TopologyService topologyService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected LinkResourceService resourceService;
+
+    @Activate
+    public void activate() {
+        intentManager.registerCompiler(PointToPointIntentWithBandwidthConstraint.class, this);
+    }
+
+    @Deactivate
+    public void deactivate() {
+        intentManager.unregisterCompiler(PointToPointIntent.class);
+    }
+
+    @Override
+    public List<Intent> compile(PointToPointIntentWithBandwidthConstraint intent) {
+        Path path = getPath(intent.ingressPoint(), intent.egressPoint(), intent.bandwidthRequest());
+
+        List<Link> links = new ArrayList<>();
+        links.add(DefaultEdgeLink.createEdgeLink(intent.ingressPoint(), true));
+        links.addAll(path.links());
+        links.add(DefaultEdgeLink.createEdgeLink(intent.egressPoint(), false));
+
+        return Arrays.asList(createPathIntent(new DefaultPath(PID, links, path.cost() + 2,
+                                                              path.annotations()),
+                                              intent));
+    }
+
+    /**
+     * Creates a path intent from the specified path and original
+     * connectivity intent.
+     *
+     * @param path   path to create an intent for
+     * @param intent original intent
+     */
+    private Intent createPathIntent(Path path,
+                                    PointToPointIntentWithBandwidthConstraint intent) {
+        LinkResourceRequest.Builder request = DefaultLinkResourceRequest.builder(intent.id(),
+                path.links())
+                // TODO - this seems awkward, maybe allow directly attaching a BandwidthRequest
+                .addBandwidthRequest(intent.bandwidthRequest().bandwidth().toDouble());
+        LinkResourceRequest bandwidthRequest  = request.build();
+        LinkResourceRequest[] bandwidthRequests = {bandwidthRequest};
+        return new PathIntent(intent.appId(),
+                              intent.selector(), intent.treatment(), path,
+                              bandwidthRequests);
+    }
+
+    /**
+     * Computes a path between two ConnectPoints.
+     *
+     * @param one start of the path
+     * @param two end of the path
+     * @return Path between the two
+     * @throws org.onlab.onos.net.intent.impl.PathNotFoundException if a path cannot be found
+     */
+    private Path getPath(ConnectPoint one, ConnectPoint two, final BandwidthResourceRequest bandwidthRequest) {
+        Topology topology = topologyService.currentTopology();
+        LinkWeight weight = new LinkWeight() {
+            @Override
+            public double weight(TopologyEdge edge) {
+                if (bandwidthRequest != null) {
+                    double allocatedBandwidth = 0.0;
+                    Iterable<ResourceRequest> availableResources = resourceService.getAvailableResources(edge.link());
+                    for (ResourceRequest availableResource : availableResources) {
+                        if (availableResource.type() == ResourceType.BANDWIDTH) {
+                            BandwidthResourceRequest bandwidthRequest = (BandwidthResourceRequest) availableResource;
+                            allocatedBandwidth += bandwidthRequest.bandwidth().toDouble();
+                        }
+                    }
+
+                    // TODO this needs to be discovered from switch/ports somehow
+                    double maxBandwidth = 1000;
+
+                    double availableBandwidth = maxBandwidth - allocatedBandwidth;
+                    if (availableBandwidth >= bandwidthRequest.bandwidth().toDouble()) {
+                        return 1;
+                    } else {
+                        return -1;
+                    }
+                } else {
+                    return 1;
+                }
+            }
+        };
+
+        Set<Path> paths = topologyService.getPaths(topology,
+                one.deviceId(),
+                two.deviceId(),
+                weight);
+
+        if (paths.isEmpty()) {
+            throw new PathNotFoundException("No packet path from " + one + " to " + two);
+        }
+        // TODO: let's be more intelligent about this eventually
+        return paths.iterator().next();
+    }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/resource/impl/LinkResourceManager.java b/core/net/src/main/java/org/onlab/onos/net/resource/impl/LinkResourceManager.java
index bdd0761..4f84b82 100644
--- a/core/net/src/main/java/org/onlab/onos/net/resource/impl/LinkResourceManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/resource/impl/LinkResourceManager.java
@@ -17,6 +17,7 @@
 
 import static org.slf4j.LoggerFactory.getLogger;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
@@ -50,6 +51,8 @@
 
     private final Logger log = getLogger(getClass());
 
+    LinkResourceAllocations savedAllocations;
+
     @Activate
     public void activate() {
         log.info("Started");
@@ -64,6 +67,8 @@
         return Sets.newHashSet(Lambda.valueOf(7));
     }
 
+    double usedBandwidth = 0.0;
+
     @Override
     public LinkResourceAllocations requestResources(LinkResourceRequest req) {
         // TODO implement it using a resource data store.
@@ -75,6 +80,7 @@
                 log.info("requestResources() always returns requested bandwidth");
                 BandwidthResourceRequest br = (BandwidthResourceRequest) r;
                 alloc = new BandwidthResourceAllocation(br.bandwidth());
+                usedBandwidth += br.bandwidth().toDouble();
                 break;
             case LAMBDA:
                 log.info("requestResources() always returns lambda 7");
@@ -92,7 +98,8 @@
         for (Link link : req.links()) {
             allocations.put(link, Sets.newHashSet(alloc));
         }
-        return new DefaultLinkResourceAllocations(req, allocations);
+        savedAllocations = new DefaultLinkResourceAllocations(req, allocations);
+        return savedAllocations;
     }
 
     @Override
@@ -115,8 +122,11 @@
 
     @Override
     public Iterable<LinkResourceAllocations> getAllocations(Link link) {
-        // TODO Auto-generated method stub
-        return null;
+        ArrayList<LinkResourceAllocations> retval = new ArrayList<>(0);
+        if (savedAllocations != null) {
+            retval.add(savedAllocations);
+        }
+        return retval;
     }
 
     @Override
@@ -127,8 +137,10 @@
 
     @Override
     public Iterable<ResourceRequest> getAvailableResources(Link link) {
-        // TODO Auto-generated method stub
-        return null;
+        BandwidthResourceRequest bw = new BandwidthResourceRequest(usedBandwidth);
+        ArrayList<ResourceRequest> result = new ArrayList<>();
+        result.add(bw);
+        return result;
     }
 
     @Override