ONOS-22 - Add Constraints to CLI Commands

Added optional --lambda and --bandwidth operations to command
line for host to host, point to point, and multi point to
single point intents

Fixed intent compilers to add constraints to the PathIntents they
create

Change-Id: I25510d401118feba493f51ecddc72d770d8ae3e3
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());
     }
 
 }