Merge "ONOS-22 - Add Constraints to CLI Commands"
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/AddHostToHostIntentCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/AddHostToHostIntentCommand.java
index 7d3bfc2..13f3000 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/AddHostToHostIntentCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/AddHostToHostIntentCommand.java
@@ -15,14 +15,16 @@
  */
 package org.onlab.onos.cli.net;
 
+import java.util.List;
+
 import org.apache.karaf.shell.commands.Argument;
 import org.apache.karaf.shell.commands.Command;
-import org.onlab.onos.cli.AbstractShellCommand;
 import org.onlab.onos.net.HostId;
 import org.onlab.onos.net.flow.DefaultTrafficSelector;
 import org.onlab.onos.net.flow.DefaultTrafficTreatment;
 import org.onlab.onos.net.flow.TrafficSelector;
 import org.onlab.onos.net.flow.TrafficTreatment;
+import org.onlab.onos.net.intent.Constraint;
 import org.onlab.onos.net.intent.HostToHostIntent;
 import org.onlab.onos.net.intent.IntentService;
 
@@ -31,7 +33,7 @@
  */
 @Command(scope = "onos", name = "add-host-intent",
          description = "Installs host-to-host connectivity intent")
-public class AddHostToHostIntentCommand extends AbstractShellCommand {
+public class AddHostToHostIntentCommand extends ConnectivityIntentCommand {
 
     @Argument(index = 0, name = "one", description = "One host ID",
               required = true, multiValued = false)
@@ -50,9 +52,11 @@
 
         TrafficSelector selector = DefaultTrafficSelector.builder().build();
         TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
+        List<Constraint> constraints = buildConstraints();
 
         HostToHostIntent intent = new HostToHostIntent(appId(), oneId, twoId,
-                                                       selector, treatment);
+                                                       selector, treatment,
+                                                       constraints);
         service.submit(intent);
     }
 
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/AddMultiPointToSinglePointIntentCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/AddMultiPointToSinglePointIntentCommand.java
index 9e8e2fc..1cd695e 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/AddMultiPointToSinglePointIntentCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/AddMultiPointToSinglePointIntentCommand.java
@@ -23,11 +23,13 @@
 import org.onlab.onos.net.flow.DefaultTrafficTreatment;
 import org.onlab.onos.net.flow.TrafficSelector;
 import org.onlab.onos.net.flow.TrafficTreatment;
+import org.onlab.onos.net.intent.Constraint;
 import org.onlab.onos.net.intent.Intent;
 import org.onlab.onos.net.intent.IntentService;
 import org.onlab.onos.net.intent.MultiPointToSinglePointIntent;
 
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 
 import static org.onlab.onos.net.DeviceId.deviceId;
@@ -69,9 +71,11 @@
 
         TrafficSelector selector = buildTrafficSelector();
         TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
+        List<Constraint> constraints = buildConstraints();
 
         Intent intent = new MultiPointToSinglePointIntent(appId(), selector, treatment,
-                                                          ingressPoints, egress);
+                                                          ingressPoints, egress,
+                                                          constraints);
         service.submit(intent);
     }
 
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/AddPointToPointIntentCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/AddPointToPointIntentCommand.java
index ed98c7e..26bb1c0 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/AddPointToPointIntentCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/AddPointToPointIntentCommand.java
@@ -15,6 +15,8 @@
  */
 package org.onlab.onos.cli.net;
 
+import java.util.List;
+
 import org.apache.karaf.shell.commands.Argument;
 import org.apache.karaf.shell.commands.Command;
 import org.onlab.onos.net.ConnectPoint;
@@ -22,6 +24,7 @@
 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.Constraint;
 import org.onlab.onos.net.intent.Intent;
 import org.onlab.onos.net.intent.IntentService;
 import org.onlab.onos.net.intent.PointToPointIntent;
@@ -63,8 +66,10 @@
         TrafficSelector selector = buildTrafficSelector();
         TrafficTreatment treatment = builder().build();
 
+        List<Constraint> constraints = buildConstraints();
+
         Intent intent = new PointToPointIntent(appId(), selector, treatment,
-                                               ingress, egress);
+                                               ingress, egress, constraints);
         service.submit(intent);
     }
 
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 d8ec3c7..e4fc5aa 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
@@ -15,14 +15,21 @@
  */
 package org.onlab.onos.cli.net;
 
+import java.util.LinkedList;
+import java.util.List;
+
 import org.apache.karaf.shell.commands.Option;
 import org.onlab.onos.cli.AbstractShellCommand;
 import org.onlab.onos.net.flow.DefaultTrafficSelector;
 import org.onlab.onos.net.flow.TrafficSelector;
+import org.onlab.onos.net.intent.Constraint;
+import org.onlab.onos.net.intent.constraint.BandwidthConstraint;
+import org.onlab.onos.net.intent.constraint.LambdaConstraint;
+import org.onlab.onos.net.resource.Bandwidth;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.MacAddress;
 
-import com.google.common.base.Strings;
+import static com.google.common.base.Strings.isNullOrEmpty;
 
 /**
  * Base class for command line operations for connectivity based intents.
@@ -41,6 +48,14 @@
             required = false, multiValued = false)
     private String ethTypeString = "";
 
+    @Option(name = "-b", aliases = "--bandwidth", description = "Bandwidth",
+            required = false, multiValued = false)
+    private String bandwidthString = "";
+
+    @Option(name = "-l", aliases = "--lambda", description = "Lambda",
+            required = false, multiValued = false)
+    private boolean lambda = false;
+
     /**
      * Constructs a traffic selector based on the command line arguments
      * presented to the command.
@@ -50,21 +65,43 @@
         TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
         Short ethType = Ethernet.TYPE_IPV4;
 
-        if (!Strings.isNullOrEmpty(ethTypeString)) {
+        if (!isNullOrEmpty(ethTypeString)) {
             EthType ethTypeParameter = EthType.valueOf(ethTypeString);
             ethType = ethTypeParameter.value();
         }
         selectorBuilder.matchEthType(ethType);
 
-        if (!Strings.isNullOrEmpty(srcMacString)) {
+        if (!isNullOrEmpty(srcMacString)) {
             selectorBuilder.matchEthSrc(MacAddress.valueOf(srcMacString));
         }
 
-        if (!Strings.isNullOrEmpty(dstMacString)) {
+        if (!isNullOrEmpty(dstMacString)) {
             selectorBuilder.matchEthDst(MacAddress.valueOf(dstMacString));
         }
 
         return selectorBuilder.build();
     }
 
+    /**
+     * Builds the constraint list for this command based on the command line
+     * parameters.
+     *
+     * @return List of constraint objects describing the constraints requested
+     */
+    protected List<Constraint> buildConstraints() {
+        final List<Constraint> constraints = new LinkedList<>();
+
+        // Check for a bandwidth specification
+        if (!isNullOrEmpty(bandwidthString)) {
+            final double bandwidthValue = Double.parseDouble(bandwidthString);
+            constraints.add(new BandwidthConstraint(Bandwidth.valueOf(bandwidthValue)));
+        }
+
+        // Check for a lambda specification
+        if (lambda) {
+            constraints.add(new LambdaConstraint(null));
+        }
+
+        return constraints;
+    }
 }
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/HostToHostIntent.java b/core/api/src/main/java/org/onlab/onos/net/intent/HostToHostIntent.java
index 3fad93d..893270a 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/HostToHostIntent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/HostToHostIntent.java
@@ -105,6 +105,7 @@
                 .add("appId", appId())
                 .add("selector", selector())
                 .add("treatment", treatment())
+                .add("constraints", constraints())
                 .add("one", one)
                 .add("two", two)
                 .toString();
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/MultiPointToSinglePointIntent.java b/core/api/src/main/java/org/onlab/onos/net/intent/MultiPointToSinglePointIntent.java
index 0c47aad..90907fb 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/MultiPointToSinglePointIntent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/MultiPointToSinglePointIntent.java
@@ -22,6 +22,7 @@
 import org.onlab.onos.net.flow.TrafficSelector;
 import org.onlab.onos.net.flow.TrafficTreatment;
 
+import java.util.List;
 import java.util.Set;
 
 import static com.google.common.base.Preconditions.checkArgument;
@@ -65,6 +66,38 @@
     }
 
     /**
+     * Creates a new multi-to-single point connectivity intent for the specified
+     * traffic selector and treatment.
+     *
+     * @param appId         application identifier
+     * @param selector      traffic selector
+     * @param treatment     treatment
+     * @param ingressPoints set of ports from which ingress traffic originates
+     * @param egressPoint   port to which traffic will egress
+     * @param constraints   constraints to apply to the intent
+     * @throws NullPointerException     if {@code ingressPoints} or
+     *                                  {@code egressPoint} is null.
+     * @throws IllegalArgumentException if the size of {@code ingressPoints} is
+     *                                  not more than 1
+     */
+    public MultiPointToSinglePointIntent(ApplicationId appId,
+                                         TrafficSelector selector,
+                                         TrafficTreatment treatment,
+                                         Set<ConnectPoint> ingressPoints,
+                                         ConnectPoint egressPoint,
+                                         List<Constraint> constraints) {
+        super(id(MultiPointToSinglePointIntent.class, selector, treatment,
+                ingressPoints, egressPoint), appId, null, selector, treatment,
+                constraints);
+
+        checkNotNull(ingressPoints);
+        checkArgument(!ingressPoints.isEmpty(), "Ingress point set cannot be empty");
+
+        this.ingressPoints = Sets.newHashSet(ingressPoints);
+        this.egressPoint = checkNotNull(egressPoint);
+    }
+
+    /**
      * Constructor for serializer.
      */
     protected MultiPointToSinglePointIntent() {
@@ -101,6 +134,7 @@
                 .add("treatment", treatment())
                 .add("ingress", ingressPoints())
                 .add("egress", egressPoint())
+                .add("constraints", constraints())
                 .toString();
     }
 }
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 9189bae..9f8816d 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
@@ -15,6 +15,8 @@
  */
 package org.onlab.onos.net.intent;
 
+import java.util.List;
+
 import com.google.common.base.MoreObjects;
 import org.onlab.onos.core.ApplicationId;
 import org.onlab.onos.net.Path;
@@ -46,6 +48,24 @@
     }
 
     /**
+     * Creates a new point-to-point intent with the supplied ingress/egress
+     * ports and using the specified explicit path.
+     *
+     * @param appId     application identifier
+     * @param selector  traffic selector
+     * @param treatment treatment
+     * @param path      traversed links
+     * @param constraints  optional list of constraints
+     * @throws NullPointerException {@code path} is null
+     */
+    public PathIntent(ApplicationId appId, TrafficSelector selector,
+                      TrafficTreatment treatment, Path path, List<Constraint> constraints) {
+        super(id(PathIntent.class, selector, treatment, path, constraints), appId,
+                resources(path.links()), selector, treatment, constraints);
+        this.path = path;
+    }
+
+    /**
      * Constructor for serializer.
      */
     protected PathIntent() {
@@ -75,6 +95,7 @@
                 .add("appId", appId())
                 .add("selector", selector())
                 .add("treatment", treatment())
+                .add("constraints", constraints())
                 .add("path", path)
                 .toString();
     }
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/ConnectivityIntentCompiler.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/ConnectivityIntentCompiler.java
index 4cf1830..8a31900 100644
--- a/core/net/src/main/java/org/onlab/onos/net/intent/impl/ConnectivityIntentCompiler.java
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/ConnectivityIntentCompiler.java
@@ -118,13 +118,14 @@
 
         @Override
         public double weight(TopologyEdge edge) {
-            if (constraints == null) {
+            if (constraints == null || !constraints.iterator().hasNext()) {
                 return 1.0;
             }
 
             // iterate over all constraints in order and return the weight of
             // the first one with fast fail over the first failure
             Iterator<Constraint> it = constraints.iterator();
+
             double cost = it.next().cost(edge.link(), resourceService);
             while (it.hasNext() && cost > 0) {
                 if (it.next().cost(edge.link(), resourceService) < 0) {
@@ -132,6 +133,7 @@
                 }
             }
             return cost;
+
         }
     }
 
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 605d3c7..4d989ac 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
@@ -70,7 +70,8 @@
                                     HostToHostIntent intent) {
         TrafficSelector selector = builder(intent.selector())
                 .matchEthSrc(src.mac()).matchEthDst(dst.mac()).build();
-        return new PathIntent(intent.appId(), selector, intent.treatment(), path);
+        return new PathIntent(intent.appId(), selector, intent.treatment(),
+                              path, intent.constraints());
     }
 
 }
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 c32c8ee..5c8eb94 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
@@ -77,7 +77,8 @@
     private Intent createPathIntent(Path path,
                                     PointToPointIntent intent) {
         return new PathIntent(intent.appId(),
-                              intent.selector(), intent.treatment(), path);
+                              intent.selector(), intent.treatment(), path,
+                              intent.constraints());
     }
 
 }