Fix latency constraint

1. Consider that the type "Duration" in the String is composed by
characters and numbers, it's not easy to convert it to double for
comparison, I modified the latency's annotation to
cfg.latency().toNanos().
2. Exclude two EdgeLinks for the calculation of the whole path's latency
3. The unit of latency in ONOS is not the same, so I set all the latency units to
nanoseconds.
4. Add the latency constraint option for ConnectivityIntentCommand.

Change-Id: Iddf5634880e43ed563db9978659db5eb9ee6c7f8
diff --git a/cli/src/main/java/org/onosproject/cli/net/ConnectivityIntentCommand.java b/cli/src/main/java/org/onosproject/cli/net/ConnectivityIntentCommand.java
index aa3c810..514311e 100644
--- a/cli/src/main/java/org/onosproject/cli/net/ConnectivityIntentCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/ConnectivityIntentCommand.java
@@ -40,8 +40,11 @@
 import org.onosproject.net.intent.constraint.DomainConstraint;
 import org.onosproject.net.intent.constraint.EncapsulationConstraint;
 import org.onosproject.net.intent.constraint.HashedPathSelectionConstraint;
+import org.onosproject.net.intent.constraint.LatencyConstraint;
 import org.onosproject.net.intent.constraint.PartialFailureConstraint;
 
+import java.time.Duration;
+import java.time.temporal.ChronoUnit;
 import java.util.LinkedList;
 import java.util.List;
 
@@ -187,6 +190,11 @@
             required = false, multiValued = false)
     private boolean domains = false;
 
+    @Option(name = "-l", aliases = "--latency",
+            description = "Max latency in nanoseconds tolerated by the intent", required = false,
+            multiValued = false)
+    String latConstraint = null;
+
     // Resource Group
     @Option(name = "-r", aliases = "--resourceGroup", description = "Resource Group Id",
             required = false, multiValued = false)
@@ -410,6 +418,16 @@
         if (domains) {
             constraints.add(DomainConstraint.domain());
         }
+        // Check for a latency specification
+        if (!isNullOrEmpty(latConstraint)) {
+            try {
+                long lat = Long.parseLong(latConstraint);
+                constraints.add(new LatencyConstraint(Duration.of(lat, ChronoUnit.NANOS)));
+            } catch (NumberFormatException e) {
+                double lat = Double.parseDouble(latConstraint);
+                constraints.add(new LatencyConstraint(Duration.of((long) lat, ChronoUnit.NANOS)));
+            }
+        }
         return constraints;
     }
 
diff --git a/core/api/src/main/java/org/onosproject/net/AnnotationKeys.java b/core/api/src/main/java/org/onosproject/net/AnnotationKeys.java
index 4101738..665565d 100644
--- a/core/api/src/main/java/org/onosproject/net/AnnotationKeys.java
+++ b/core/api/src/main/java/org/onosproject/net/AnnotationKeys.java
@@ -101,7 +101,7 @@
 
     /**
      * Annotation key for latency.
-     * The value of this key is expected to be latency in microsecond.
+     * The value of this key is expected to be latency in nanosecond.
      */
     public static final String LATENCY = "latency";
 
diff --git a/core/api/src/main/java/org/onosproject/net/intent/constraint/LatencyConstraint.java b/core/api/src/main/java/org/onosproject/net/intent/constraint/LatencyConstraint.java
index 94ad04d..06959c4 100644
--- a/core/api/src/main/java/org/onosproject/net/intent/constraint/LatencyConstraint.java
+++ b/core/api/src/main/java/org/onosproject/net/intent/constraint/LatencyConstraint.java
@@ -62,7 +62,12 @@
     }
 
     private double cost(Link link) {
-        return getAnnotatedValue(link, LATENCY);
+        //Check only links, not EdgeLinks
+        if (link.type() != Link.Type.EDGE) {
+            return link.annotations().value(LATENCY) != null ? getAnnotatedValue(link, LATENCY) : 0;
+        } else {
+            return 0;
+        }
     }
 
     // doesn't use LinkResourceService
@@ -73,8 +78,9 @@
     }
 
     private boolean validate(Path path) {
+        //Guarantee all the latency units in ONOS is nanoseconds.
         double pathLatency = path.links().stream().mapToDouble(this::cost).sum();
-        return Duration.of((long) pathLatency, ChronoUnit.MICROS).compareTo(latency) <= 0;
+        return Duration.of((long) pathLatency, ChronoUnit.NANOS).compareTo(latency) <= 0;
     }
 
     @Override
diff --git a/core/api/src/test/java/org/onosproject/net/intent/constraint/LatencyConstraintTest.java b/core/api/src/test/java/org/onosproject/net/intent/constraint/LatencyConstraintTest.java
index c1ee3aa..8b764a9 100644
--- a/core/api/src/test/java/org/onosproject/net/intent/constraint/LatencyConstraintTest.java
+++ b/core/api/src/test/java/org/onosproject/net/intent/constraint/LatencyConstraintTest.java
@@ -91,7 +91,7 @@
      */
     @Test
     public void testLessThanLatency() {
-        sut = new LatencyConstraint(Duration.of(10, ChronoUnit.MICROS));
+        sut = new LatencyConstraint(Duration.of(10, ChronoUnit.NANOS));
 
         assertThat(sut.validate(path, resourceContext), is(true));
     }
@@ -101,7 +101,7 @@
      */
     @Test
     public void testMoreThanLatency() {
-        sut = new LatencyConstraint(Duration.of(3, ChronoUnit.MICROS));
+        sut = new LatencyConstraint(Duration.of(3, ChronoUnit.NANOS));
 
         assertThat(sut.validate(path, resourceContext), is(false));
     }
@@ -111,7 +111,7 @@
      */
     @Test
     public void testCost() {
-        sut = new LatencyConstraint(Duration.of(10, ChronoUnit.MICROS));
+        sut = new LatencyConstraint(Duration.of(10, ChronoUnit.NANOS));
 
         assertThat(sut.cost(link1, resourceContext), is(closeTo(Double.parseDouble(LATENCY1), 1.0e-6)));
         assertThat(sut.cost(link2, resourceContext), is(closeTo(Double.parseDouble(LATENCY2), 1.0e-6)));
diff --git a/core/net/src/main/java/org/onosproject/net/link/impl/BasicLinkOperator.java b/core/net/src/main/java/org/onosproject/net/link/impl/BasicLinkOperator.java
index cabe565..7795f77 100644
--- a/core/net/src/main/java/org/onosproject/net/link/impl/BasicLinkOperator.java
+++ b/core/net/src/main/java/org/onosproject/net/link/impl/BasicLinkOperator.java
@@ -82,7 +82,9 @@
             b.set(AnnotationKeys.METRIC, String.valueOf(cfg.metric()));
         }
         if (!cfg.latency().equals(DEF_DURATION)) {
-            b.set(AnnotationKeys.LATENCY, cfg.latency().toString());
+            //Convert the latency from Duration to long,
+            //so that it's computable in the latencyConstraint.
+            b.set(AnnotationKeys.LATENCY, String.valueOf(cfg.latency().toNanos()));
         }
         if (cfg.bandwidth() != DEF_BANDWIDTH) {
             b.set(AnnotationKeys.BANDWIDTH, String.valueOf(cfg.bandwidth()));
diff --git a/core/net/src/test/java/org/onosproject/net/link/impl/BasicLinkOperatorTest.java b/core/net/src/test/java/org/onosproject/net/link/impl/BasicLinkOperatorTest.java
index d540aeb..6450052 100644
--- a/core/net/src/test/java/org/onosproject/net/link/impl/BasicLinkOperatorTest.java
+++ b/core/net/src/test/java/org/onosproject/net/link/impl/BasicLinkOperatorTest.java
@@ -47,7 +47,7 @@
     private static final ConnectPoint SRC = new ConnectPoint(DID1, P1);
     private static final ConnectPoint DST = new ConnectPoint(DID2, P1);
     private static final LinkKey LK = LinkKey.linkKey(SRC, DST);
-    private static final Duration NTIME = Duration.ofNanos(200);
+    private static final long NTIME = 200;
 
     private static final SparseAnnotations SA = DefaultAnnotations.builder()
             .set(AnnotationKeys.DURABLE, "true").build();
@@ -60,13 +60,13 @@
     @Before
     public void setUp() {
         BLC.init(LK, "optest", JsonNodeFactory.instance.objectNode(), mapper, delegate);
-        BLC.latency(NTIME);
+        BLC.latency(Duration.ofNanos(NTIME));
     }
 
     @Test
     public void testDescOps() {
         LinkDescription desc = BasicLinkOperator.combine(BLC, LD);
-        assertEquals(NTIME.toString(), desc.annotations().value(AnnotationKeys.LATENCY));
+        assertEquals(String.valueOf(NTIME), desc.annotations().value(AnnotationKeys.LATENCY));
         assertEquals("true", desc.annotations().value(AnnotationKeys.DURABLE));
     }
 }