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/apps/calendar/src/main/java/org/onlab/onos/calendar/BandwidthCalendarResource.java b/apps/calendar/src/main/java/org/onlab/onos/calendar/BandwidthCalendarResource.java
index c534371..cd8cc5c 100644
--- a/apps/calendar/src/main/java/org/onlab/onos/calendar/BandwidthCalendarResource.java
+++ b/apps/calendar/src/main/java/org/onlab/onos/calendar/BandwidthCalendarResource.java
@@ -15,18 +15,29 @@
  */
 package org.onlab.onos.calendar;
 
-import org.onlab.onos.net.ConnectPoint;
-import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.net.intent.IntentService;
-import org.onlab.rest.BaseResource;
+import java.net.URI;
 
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.core.Response;
-import java.net.URI;
+
+import org.onlab.onos.core.ApplicationId;
+import org.onlab.onos.core.CoreService;
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.flow.DefaultTrafficSelector;
+import org.onlab.onos.net.flow.TrafficSelector;
+import org.onlab.onos.net.flow.TrafficTreatment;
+import org.onlab.onos.net.intent.Intent;
+import org.onlab.onos.net.intent.IntentService;
+import org.onlab.onos.net.intent.PointToPointIntentWithBandwidthConstraint;
+import org.onlab.onos.net.resource.BandwidthResourceRequest;
+import org.onlab.packet.Ethernet;
+import org.onlab.rest.BaseResource;
 
 import static org.onlab.onos.net.PortNumber.portNumber;
+import static org.onlab.onos.net.flow.DefaultTrafficTreatment.builder;
 
 /**
  * Web resource for triggering calendared intents.
@@ -47,12 +58,33 @@
         ConnectPoint srcPoint = new ConnectPoint(deviceId(src), portNumber(srcPort));
         ConnectPoint dstPoint = new ConnectPoint(deviceId(dst), portNumber(dstPort));
 
+        TrafficSelector selector = buildTrafficSelector();
+        TrafficTreatment treatment = builder().build();
+
+        Intent intent = new PointToPointIntentWithBandwidthConstraint(
+                appId(), selector, treatment,
+                srcPoint, dstPoint, new BandwidthResourceRequest(Double.parseDouble(bandwidth)));
+        service.submit(intent);
+
         return Response.ok("Yo! We got src=" + srcPoint + "; dst=" + dstPoint +
                                    "; bw=" + bandwidth + "; intent service " + service).build();
     }
 
+    private TrafficSelector buildTrafficSelector() {
+        TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
+        Short ethType = Ethernet.TYPE_IPV4;
+
+        selectorBuilder.matchEthType(ethType);
+
+        return selectorBuilder.build();
+    }
+
     private DeviceId deviceId(String dpid) {
         return DeviceId.deviceId(URI.create("of:" + dpid));
     }
 
+    protected ApplicationId appId() {
+        return get(CoreService.class).registerApplication("org.onlab.onos.calendar");
+    }
+
 }
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/AddPointToPointIntentWithBandwidthConstraintCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/AddPointToPointIntentWithBandwidthConstraintCommand.java
new file mode 100644
index 0000000..1207f1a
--- /dev/null
+++ b/cli/src/main/java/org/onlab/onos/cli/net/AddPointToPointIntentWithBandwidthConstraintCommand.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.onlab.onos.cli.net;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.PortNumber;
+import org.onlab.onos.net.flow.TrafficSelector;
+import org.onlab.onos.net.flow.TrafficTreatment;
+import org.onlab.onos.net.intent.Intent;
+import org.onlab.onos.net.intent.IntentService;
+import org.onlab.onos.net.intent.PointToPointIntentWithBandwidthConstraint;
+import org.onlab.onos.net.resource.BandwidthResourceRequest;
+
+import static org.onlab.onos.net.DeviceId.deviceId;
+import static org.onlab.onos.net.PortNumber.portNumber;
+import static org.onlab.onos.net.flow.DefaultTrafficTreatment.builder;
+
+/**
+ * Installs point-to-point connectivity intents.
+ */
+@Command(scope = "onos", name = "add-point-intent-bw",
+         description = "Installs point-to-point connectivity intent with bandwidth constraint")
+public class AddPointToPointIntentWithBandwidthConstraintCommand extends ConnectivityIntentCommand {
+
+    @Argument(index = 0, name = "ingressDevice",
+              description = "Ingress Device/Port Description",
+              required = true, multiValued = false)
+    String ingressDeviceString = null;
+
+    @Argument(index = 1, name = "egressDevice",
+              description = "Egress Device/Port Description",
+              required = true, multiValued = false)
+    String egressDeviceString = null;
+
+    @Argument(index = 2, name = "bandwidth",
+            description = "Bandwidth",
+            required = true, multiValued = false)
+    String bandwidthString = null;
+
+    @Override
+    protected void execute() {
+        IntentService service = get(IntentService.class);
+
+        DeviceId ingressDeviceId = deviceId(getDeviceId(ingressDeviceString));
+        PortNumber ingressPortNumber = portNumber(getPortNumber(ingressDeviceString));
+        ConnectPoint ingress = new ConnectPoint(ingressDeviceId, ingressPortNumber);
+
+        DeviceId egressDeviceId = deviceId(getDeviceId(egressDeviceString));
+        PortNumber egressPortNumber = portNumber(getPortNumber(egressDeviceString));
+        ConnectPoint egress = new ConnectPoint(egressDeviceId, egressPortNumber);
+
+        long bandwidth = Long.parseLong(bandwidthString);
+
+        TrafficSelector selector = buildTrafficSelector();
+        TrafficTreatment treatment = builder().build();
+
+        Intent intent = new PointToPointIntentWithBandwidthConstraint(
+                appId(), selector, treatment,
+                ingress, egress, new BandwidthResourceRequest(bandwidth));
+        service.submit(intent);
+    }
+
+    /**
+     * Extracts the port number portion of the ConnectPoint.
+     *
+     * @param deviceString string representing the device/port
+     * @return port number as a string, empty string if the port is not found
+     */
+    private String getPortNumber(String deviceString) {
+        int slash = deviceString.indexOf('/');
+        if (slash <= 0) {
+            return "";
+        }
+        return deviceString.substring(slash + 1, deviceString.length());
+    }
+
+    /**
+     * Extracts the device ID portion of the ConnectPoint.
+     *
+     * @param deviceString string representing the device/port
+     * @return device ID string
+     */
+    private String getDeviceId(String deviceString) {
+        int slash = deviceString.indexOf('/');
+        if (slash <= 0) {
+            return "";
+        }
+        return deviceString.substring(0, slash);
+    }
+}
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/ConnectivityIntentCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/ConnectivityIntentCommand.java
index b9b071f..b61a646 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/ConnectivityIntentCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/ConnectivityIntentCommand.java
@@ -47,8 +47,8 @@
      */
     protected TrafficSelector buildTrafficSelector() {
         TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
-
         Short ethType = Ethernet.TYPE_IPV4;
+
         if (!Strings.isNullOrEmpty(ethTypeString)) {
             EthType ethTypeParameter = EthType.valueOf(ethTypeString);
             ethType = ethTypeParameter.value();
diff --git a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index 418ee26..f8b6a13 100644
--- a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -116,6 +116,17 @@
             </optional-completers>
         </command>
         <command>
+            <action class="org.onlab.onos.cli.net.AddPointToPointIntentWithBandwidthConstraintCommand"/>
+            <completers>
+                <ref component-id="connectPointCompleter"/>
+                <ref component-id="connectPointCompleter"/>
+                <null/>
+            </completers>
+            <optional-completers>
+                <entry key="-t" value-ref="ethTypeCompleter"/>
+            </optional-completers>
+        </command>
+        <command>
             <action class="org.onlab.onos.cli.net.AddOpticalIntentCommand"/>
             <completers>
                 <ref component-id="connectPointCompleter"/>
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/PathIntent.java b/core/api/src/main/java/org/onlab/onos/net/intent/PathIntent.java
index 721a8fd..0789db0 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/PathIntent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/PathIntent.java
@@ -20,6 +20,7 @@
 import org.onlab.onos.net.Path;
 import org.onlab.onos.net.flow.TrafficSelector;
 import org.onlab.onos.net.flow.TrafficTreatment;
+import org.onlab.onos.net.resource.LinkResourceRequest;
 
 /**
  * Abstraction of explicitly path specified connectivity intent.
@@ -27,6 +28,7 @@
 public class PathIntent extends ConnectivityIntent {
 
     private final Path path;
+    private final LinkResourceRequest[] resourceRequests;
 
     /**
      * Creates a new point-to-point intent with the supplied ingress/egress
@@ -39,10 +41,11 @@
      * @throws NullPointerException {@code path} is null
      */
     public PathIntent(ApplicationId appId, TrafficSelector selector,
-                      TrafficTreatment treatment, Path path) {
+                      TrafficTreatment treatment, Path path, LinkResourceRequest[] resourceRequests) {
         super(id(PathIntent.class, selector, treatment, path), appId,
               resources(path.links()), selector, treatment);
         this.path = path;
+        this.resourceRequests = resourceRequests;
     }
 
     /**
@@ -51,6 +54,7 @@
     protected PathIntent() {
         super();
         this.path = null;
+        this.resourceRequests = new LinkResourceRequest[0];
     }
 
     /**
@@ -67,6 +71,10 @@
         return true;
     }
 
+    public LinkResourceRequest[] resourceRequests() {
+        return resourceRequests;
+    }
+
     @Override
     public String toString() {
         return MoreObjects.toStringHelper(getClass())
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/PointToPointIntentWithBandwidthConstraint.java b/core/api/src/main/java/org/onlab/onos/net/intent/PointToPointIntentWithBandwidthConstraint.java
new file mode 100644
index 0000000..d5c4d57
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/PointToPointIntentWithBandwidthConstraint.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.onlab.onos.net.intent;
+
+import org.onlab.onos.core.ApplicationId;
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.flow.TrafficSelector;
+import org.onlab.onos.net.flow.TrafficTreatment;
+import org.onlab.onos.net.resource.BandwidthResourceRequest;
+
+import com.google.common.base.MoreObjects;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Abstraction of point-to-point connectivity.
+ */
+public class PointToPointIntentWithBandwidthConstraint extends ConnectivityIntent {
+
+    private final ConnectPoint ingressPoint;
+    private final ConnectPoint egressPoint;
+    private final BandwidthResourceRequest bandwidthResourceRequest;
+
+    /**
+     * Creates a new point-to-point intent with the supplied ingress/egress
+     * ports.
+     *
+     * @param appId        application identifier
+     * @param selector     traffic selector
+     * @param treatment    treatment
+     * @param ingressPoint ingress port
+     * @param egressPoint  egress port
+     * @throws NullPointerException if {@code ingressPoint} or {@code egressPoints} is null.
+     */
+    public PointToPointIntentWithBandwidthConstraint(ApplicationId appId, TrafficSelector selector,
+                                                     TrafficTreatment treatment,
+                                                     ConnectPoint ingressPoint,
+                                                     ConnectPoint egressPoint,
+                                                     BandwidthResourceRequest bandwidthResourceRequest) {
+        super(id(PointToPointIntentWithBandwidthConstraint.class, selector,
+                 treatment, ingressPoint, egressPoint, bandwidthResourceRequest.bandwidth()),
+              appId, null, selector, treatment);
+        this.ingressPoint = checkNotNull(ingressPoint);
+        this.egressPoint = checkNotNull(egressPoint);
+        this.bandwidthResourceRequest = bandwidthResourceRequest;
+    }
+
+    /**
+     * Constructor for serializer.
+     */
+    protected PointToPointIntentWithBandwidthConstraint() {
+        super();
+        this.ingressPoint = null;
+        this.egressPoint = null;
+        bandwidthResourceRequest = new BandwidthResourceRequest(0.0);
+    }
+
+    /**
+     * Returns the port on which the ingress traffic should be connected to
+     * the egress.
+     *
+     * @return ingress port
+     */
+    public ConnectPoint ingressPoint() {
+        return ingressPoint;
+    }
+
+    /**
+     * Returns the port on which the traffic should egress.
+     *
+     * @return egress port
+     */
+    public ConnectPoint egressPoint() {
+        return egressPoint;
+    }
+
+    public BandwidthResourceRequest bandwidthRequest() {
+        return this.bandwidthResourceRequest;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("id", id())
+                .add("appId", appId())
+                .add("selector", selector())
+                .add("treatment", treatment())
+                .add("ingress", ingressPoint)
+                .add("egress", egressPoint)
+                .add("bandwidth", bandwidthResourceRequest.bandwidth().toString())
+                .toString();
+    }
+
+}
diff --git a/core/api/src/test/java/org/onlab/onos/net/intent/PathIntentTest.java b/core/api/src/test/java/org/onlab/onos/net/intent/PathIntentTest.java
index 21efe49..02ca0f5 100644
--- a/core/api/src/test/java/org/onlab/onos/net/intent/PathIntentTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/intent/PathIntentTest.java
@@ -15,11 +15,12 @@
  */
 package org.onlab.onos.net.intent;
 
-import static org.junit.Assert.assertEquals;
-
 import org.junit.Test;
 import org.onlab.onos.net.NetTestTools;
 import org.onlab.onos.net.Path;
+import org.onlab.onos.net.resource.LinkResourceRequest;
+
+import static org.junit.Assert.assertEquals;
 
 public class PathIntentTest extends ConnectivityIntentTest {
     // 111:11 --> 222:22
@@ -39,11 +40,11 @@
 
     @Override
     protected PathIntent createOne() {
-        return new PathIntent(APPID, MATCH, NOP, PATH1);
+        return new PathIntent(APPID, MATCH, NOP, PATH1, new LinkResourceRequest[0]);
     }
 
     @Override
     protected PathIntent createAnother() {
-        return new PathIntent(APPID, MATCH, NOP, PATH2);
+        return new PathIntent(APPID, MATCH, NOP, PATH2, new LinkResourceRequest[0]);
     }
 }
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