Merge "Add a constructor in LinkCollectionIntent to have constraints"
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpConstants.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpConstants.java
index 596720c..33fe411 100644
--- a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpConstants.java
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpConstants.java
@@ -168,6 +168,12 @@
/** BGP UPDATE AS_PATH Type: AS_SEQUENCE. */
public static final int AS_SEQUENCE = 2;
+ /** BGP UPDATE AS_PATH Type: AS_CONFED_SEQUENCE. */
+ public static final int AS_CONFED_SEQUENCE = 3;
+
+ /** BGP UPDATE AS_PATH Type: AS_CONFED_SET. */
+ public static final int AS_CONFED_SET = 4;
+
/**
* Gets the BGP AS_PATH type as a string.
*
@@ -184,6 +190,12 @@
case AS_SEQUENCE:
typeString = "AS_SEQUENCE";
break;
+ case AS_CONFED_SEQUENCE:
+ typeString = "AS_CONFED_SEQUENCE";
+ break;
+ case AS_CONFED_SET:
+ typeString = "AS_CONFED_SET";
+ break;
default:
break;
}
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpRouteEntry.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpRouteEntry.java
index 203e03c..4ca7da6 100644
--- a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpRouteEntry.java
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpRouteEntry.java
@@ -21,6 +21,7 @@
import java.util.Objects;
import org.onlab.onos.sdnip.RouteEntry;
+import org.onlab.onos.sdnip.bgp.BgpConstants.Update;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.Ip4Prefix;
@@ -35,8 +36,7 @@
private final byte origin; // Route ORIGIN: IGP, EGP, INCOMPLETE
private final AsPath asPath; // The AS Path
private final long localPref; // The local preference for the route
- private long multiExitDisc =
- BgpConstants.Update.MultiExitDisc.LOWEST_MULTI_EXIT_DISC;
+ private long multiExitDisc = Update.MultiExitDisc.LOWEST_MULTI_EXIT_DISC;
/**
* Class constructor.
@@ -116,21 +116,33 @@
* Tests whether the route is originated from the local AS.
* <p>
* The route is considered originated from the local AS if the AS Path
- * is empty or if it begins with an AS_SET.
+ * is empty or if it begins with an AS_SET (after skipping
+ * AS_CONFED_SEQUENCE and AS_CONFED_SET).
* </p>
*
* @return true if the route is originated from the local AS, otherwise
* false
*/
boolean isLocalRoute() {
- if (asPath.getPathSegments().isEmpty()) {
+ PathSegment firstPathSegment = null;
+
+ // Find the first Path Segment by ignoring the AS_CONFED_* segments
+ for (PathSegment pathSegment : asPath.getPathSegments()) {
+ if ((pathSegment.getType() == Update.AsPath.AS_SET) ||
+ (pathSegment.getType() == Update.AsPath.AS_SEQUENCE)) {
+ firstPathSegment = pathSegment;
+ break;
+ }
+ }
+ if (firstPathSegment == null) {
+ return true; // Local route: no path segments
+ }
+ // If the first path segment is AS_SET, the route is considered local
+ if (firstPathSegment.getType() == Update.AsPath.AS_SET) {
return true;
}
- PathSegment firstPathSegment = asPath.getPathSegments().get(0);
- if (firstPathSegment.getType() == BgpConstants.Update.AsPath.AS_SET) {
- return true;
- }
- return false;
+
+ return false; // The route is not local
}
/**
@@ -143,10 +155,25 @@
* @return the BGP Neighbor AS number the route was received from.
*/
long getNeighborAs() {
+ PathSegment firstPathSegment = null;
+
if (isLocalRoute()) {
return BgpConstants.BGP_AS_0;
}
- PathSegment firstPathSegment = asPath.getPathSegments().get(0);
+
+ // Find the first Path Segment by ignoring the AS_CONFED_* segments
+ for (PathSegment pathSegment : asPath.getPathSegments()) {
+ if ((pathSegment.getType() == Update.AsPath.AS_SET) ||
+ (pathSegment.getType() == Update.AsPath.AS_SEQUENCE)) {
+ firstPathSegment = pathSegment;
+ break;
+ }
+ }
+ if (firstPathSegment == null) {
+ // NOTE: Shouldn't happen - should be captured by isLocalRoute()
+ return BgpConstants.BGP_AS_0;
+ }
+
if (firstPathSegment.getSegmentAsNumbers().isEmpty()) {
// TODO: Shouldn't happen. Should check during the parsing.
return BgpConstants.BGP_AS_0;
@@ -211,18 +238,16 @@
// Compare the MED if the neighbor AS is same: larger is better
medLabel: {
- boolean thisIsLocalRoute = isLocalRoute();
- if (thisIsLocalRoute != other.isLocalRoute()) {
- break medLabel; // AS number is different
+ if (isLocalRoute() || other.isLocalRoute()) {
+ // Compare MEDs for non-local routes only
+ break medLabel;
}
- if (!thisIsLocalRoute) {
- long thisNeighborAs = getNeighborAs();
- if (thisNeighborAs != other.getNeighborAs()) {
- break medLabel; // AS number is different
- }
- if (thisNeighborAs == BgpConstants.BGP_AS_0) {
- break medLabel; // Invalid AS number
- }
+ long thisNeighborAs = getNeighborAs();
+ if (thisNeighborAs != other.getNeighborAs()) {
+ break medLabel; // AS number is different
+ }
+ if (thisNeighborAs == BgpConstants.BGP_AS_0) {
+ break medLabel; // Invalid AS number
}
// Compare the MED
@@ -253,13 +278,16 @@
* A class to represent AS Path Segment.
*/
public static class PathSegment {
- private final byte type; // Segment type: AS_SET, AS_SEQUENCE
+ // Segment type: AS_SET(1), AS_SEQUENCE(2), AS_CONFED_SEQUENCE(3),
+ // AS_CONFED_SET(4)
+ private final byte type;
private final ArrayList<Long> segmentAsNumbers; // Segment AS numbers
/**
* Constructor.
*
- * @param type the Path Segment Type: 1=AS_SET, 2=AS_SEQUENCE
+ * @param type the Path Segment Type: AS_SET(1), AS_SEQUENCE(2),
+ * AS_CONFED_SEQUENCE(3), AS_CONFED_SET(4)
* @param segmentAsNumbers the Segment AS numbers
*/
PathSegment(byte type, ArrayList<Long> segmentAsNumbers) {
@@ -268,9 +296,11 @@
}
/**
- * Gets the Path Segment Type: AS_SET, AS_SEQUENCE.
+ * Gets the Path Segment Type: AS_SET(1), AS_SEQUENCE(2),
+ * AS_CONFED_SEQUENCE(3), AS_CONFED_SET(4).
*
- * @return the Path Segment Type: AS_SET, AS_SEQUENCE
+ * @return the Path Segment Type: AS_SET(1), AS_SEQUENCE(2),
+ * AS_CONFED_SEQUENCE(3), AS_CONFED_SET(4)
*/
public byte getType() {
return type;
@@ -309,7 +339,7 @@
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
- .add("type", BgpConstants.Update.AsPath.typeToString(type))
+ .add("type", Update.AsPath.typeToString(type))
.add("segmentAsNumbers", this.segmentAsNumbers)
.toString();
}
@@ -333,15 +363,27 @@
//
// Precompute the AS Path Length:
// - AS_SET counts as 1
+ // - AS_SEQUENCE counts how many AS numbers are included
+ // - AS_CONFED_SEQUENCE and AS_CONFED_SET are ignored
//
int pl = 0;
for (PathSegment pathSegment : pathSegments) {
- if (pathSegment.getType() ==
- BgpConstants.Update.AsPath.AS_SET) {
- pl++;
- continue;
+ switch (pathSegment.getType()) {
+ case Update.AsPath.AS_SET:
+ pl++; // AS_SET counts as 1
+ break;
+ case Update.AsPath.AS_SEQUENCE:
+ // Count each AS number
+ pl += pathSegment.getSegmentAsNumbers().size();
+ break;
+ case Update.AsPath.AS_CONFED_SEQUENCE:
+ break; // Ignore
+ case Update.AsPath.AS_CONFED_SET:
+ break; // Ignore
+ default:
+ // TODO: What to do if the Path Segment type is unknown?
+ break;
}
- pl += pathSegment.getSegmentAsNumbers().size();
}
asPathLength = pl;
}
@@ -444,7 +486,7 @@
.add("prefix", prefix())
.add("nextHop", nextHop())
.add("bgpId", bgpSession.getRemoteBgpId())
- .add("origin", BgpConstants.Update.Origin.typeToString(origin))
+ .add("origin", Update.Origin.typeToString(origin))
.add("asPath", asPath)
.add("localPref", localPref)
.add("multiExitDisc", multiExitDisc)
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpSession.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpSession.java
index 98477ae..e0a710a 100644
--- a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpSession.java
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpSession.java
@@ -975,6 +975,10 @@
case BgpConstants.Update.AsPath.AS_SET:
// FALLTHROUGH
case BgpConstants.Update.AsPath.AS_SEQUENCE:
+ // FALLTHROUGH
+ case BgpConstants.Update.AsPath.AS_CONFED_SEQUENCE:
+ // FALLTHROUGH
+ case BgpConstants.Update.AsPath.AS_CONFED_SET:
break;
default:
// ERROR: Invalid Path Segment Type
diff --git a/apps/sdnip/src/test/java/org/onlab/onos/sdnip/bgp/AsPathTest.java b/apps/sdnip/src/test/java/org/onlab/onos/sdnip/bgp/AsPathTest.java
index fddd895..a750431 100644
--- a/apps/sdnip/src/test/java/org/onlab/onos/sdnip/bgp/AsPathTest.java
+++ b/apps/sdnip/src/test/java/org/onlab/onos/sdnip/bgp/AsPathTest.java
@@ -28,30 +28,63 @@
*/
public class AsPathTest {
/**
+ * Generates Path Segments.
+ *
+ * @return the generated Path Segments
+ */
+ private ArrayList<BgpRouteEntry.PathSegment> generatePathSegments() {
+ ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
+ byte pathSegmentType;
+ ArrayList<Long> segmentAsNumbers;
+ BgpRouteEntry.PathSegment pathSegment;
+
+ pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_CONFED_SEQUENCE;
+ segmentAsNumbers = new ArrayList<>();
+ segmentAsNumbers.add((long) 1);
+ segmentAsNumbers.add((long) 2);
+ segmentAsNumbers.add((long) 3);
+ pathSegment =
+ new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers);
+ pathSegments.add(pathSegment);
+ //
+ pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_CONFED_SET;
+ segmentAsNumbers = new ArrayList<>();
+ segmentAsNumbers.add((long) 4);
+ segmentAsNumbers.add((long) 5);
+ segmentAsNumbers.add((long) 6);
+ pathSegment =
+ new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers);
+ pathSegments.add(pathSegment);
+ //
+ pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
+ segmentAsNumbers = new ArrayList<>();
+ segmentAsNumbers.add((long) 7);
+ segmentAsNumbers.add((long) 8);
+ segmentAsNumbers.add((long) 9);
+ pathSegment =
+ new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers);
+ pathSegments.add(pathSegment);
+ //
+ pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_SET;
+ segmentAsNumbers = new ArrayList<>();
+ segmentAsNumbers.add((long) 10);
+ segmentAsNumbers.add((long) 11);
+ segmentAsNumbers.add((long) 12);
+ pathSegment =
+ new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers);
+ pathSegments.add(pathSegment);
+
+ return pathSegments;
+ }
+
+ /**
* Generates an AS Path.
*
* @return a generated AS Path
*/
private BgpRouteEntry.AsPath generateAsPath() {
- ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
- byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
- ArrayList<Long> segmentAsNumbers1 = new ArrayList<>();
- segmentAsNumbers1.add((long) 1);
- segmentAsNumbers1.add((long) 2);
- segmentAsNumbers1.add((long) 3);
- BgpRouteEntry.PathSegment pathSegment1 =
- new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1);
- pathSegments.add(pathSegment1);
- //
- byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET;
- ArrayList<Long> segmentAsNumbers2 = new ArrayList<>();
- segmentAsNumbers2.add((long) 4);
- segmentAsNumbers2.add((long) 5);
- segmentAsNumbers2.add((long) 6);
- BgpRouteEntry.PathSegment pathSegment2 =
- new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2);
- pathSegments.add(pathSegment2);
- //
+ ArrayList<BgpRouteEntry.PathSegment> pathSegments =
+ generatePathSegments();
BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments);
return asPath;
@@ -65,9 +98,11 @@
BgpRouteEntry.AsPath asPath = generateAsPath();
String expectedString =
- "AsPath{pathSegments=" +
- "[PathSegment{type=AS_SEQUENCE, segmentAsNumbers=[1, 2, 3]}, " +
- "PathSegment{type=AS_SET, segmentAsNumbers=[4, 5, 6]}]}";
+ "AsPath{pathSegments=[" +
+ "PathSegment{type=AS_CONFED_SEQUENCE, segmentAsNumbers=[1, 2, 3]}, " +
+ "PathSegment{type=AS_CONFED_SET, segmentAsNumbers=[4, 5, 6]}, " +
+ "PathSegment{type=AS_SEQUENCE, segmentAsNumbers=[7, 8, 9]}, " +
+ "PathSegment{type=AS_SET, segmentAsNumbers=[10, 11, 12]}]}";
assertThat(asPath.toString(), is(expectedString));
}
@@ -86,24 +121,8 @@
@Test
public void testGetFields() {
// Create the fields to compare against
- ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
- byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
- ArrayList<Long> segmentAsNumbers1 = new ArrayList<>();
- segmentAsNumbers1.add((long) 1);
- segmentAsNumbers1.add((long) 2);
- segmentAsNumbers1.add((long) 3);
- BgpRouteEntry.PathSegment pathSegment1 =
- new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1);
- pathSegments.add(pathSegment1);
- //
- byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET;
- ArrayList<Long> segmentAsNumbers2 = new ArrayList<>();
- segmentAsNumbers2.add((long) 4);
- segmentAsNumbers2.add((long) 5);
- segmentAsNumbers2.add((long) 6);
- BgpRouteEntry.PathSegment pathSegment2 =
- new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2);
- pathSegments.add(pathSegment2);
+ ArrayList<BgpRouteEntry.PathSegment> pathSegments =
+ generatePathSegments();
// Generate the entry to test
BgpRouteEntry.AsPath asPath = generateAsPath();
@@ -116,6 +135,11 @@
*/
@Test
public void testGetAsPathLength() {
+ //
+ // NOTE:
+ // - AS_CONFED_SEQUENCE and AS_CONFED_SET are excluded
+ // - AS_SET counts as a single hop
+ //
BgpRouteEntry.AsPath asPath = generateAsPath();
assertThat(asPath.getAsPathLength(), is(4));
@@ -145,23 +169,45 @@
// Setup AS Path 2
ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
- byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
- ArrayList<Long> segmentAsNumbers1 = new ArrayList<>();
- segmentAsNumbers1.add((long) 1);
- segmentAsNumbers1.add((long) 2);
- segmentAsNumbers1.add((long) 3);
- BgpRouteEntry.PathSegment pathSegment1 =
- new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1);
- pathSegments.add(pathSegment1);
+ byte pathSegmentType;
+ ArrayList<Long> segmentAsNumbers;
+ BgpRouteEntry.PathSegment pathSegment;
+
+ pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_CONFED_SEQUENCE;
+ segmentAsNumbers = new ArrayList<>();
+ segmentAsNumbers.add((long) 1);
+ segmentAsNumbers.add((long) 2);
+ segmentAsNumbers.add((long) 3);
+ pathSegment =
+ new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers);
+ pathSegments.add(pathSegment);
//
- byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET;
- ArrayList<Long> segmentAsNumbers2 = new ArrayList<>();
- segmentAsNumbers2.add((long) 4);
- segmentAsNumbers2.add((long) 55); // Different
- segmentAsNumbers2.add((long) 6);
- BgpRouteEntry.PathSegment pathSegment2 =
- new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2);
- pathSegments.add(pathSegment2);
+ pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_CONFED_SET;
+ segmentAsNumbers = new ArrayList<>();
+ segmentAsNumbers.add((long) 4);
+ segmentAsNumbers.add((long) 5);
+ segmentAsNumbers.add((long) 6);
+ pathSegment =
+ new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers);
+ pathSegments.add(pathSegment);
+ //
+ pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
+ segmentAsNumbers = new ArrayList<>();
+ segmentAsNumbers.add((long) 7);
+ segmentAsNumbers.add((long) 8);
+ segmentAsNumbers.add((long) 9);
+ pathSegment =
+ new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers);
+ pathSegments.add(pathSegment);
+ //
+ pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_SET;
+ segmentAsNumbers = new ArrayList<>();
+ segmentAsNumbers.add((long) 10);
+ segmentAsNumbers.add((long) 111); // Different
+ segmentAsNumbers.add((long) 12);
+ pathSegment =
+ new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers);
+ pathSegments.add(pathSegment);
//
BgpRouteEntry.AsPath asPath2 = new BgpRouteEntry.AsPath(pathSegments);
@@ -176,9 +222,11 @@
BgpRouteEntry.AsPath asPath = generateAsPath();
String expectedString =
- "AsPath{pathSegments=" +
- "[PathSegment{type=AS_SEQUENCE, segmentAsNumbers=[1, 2, 3]}, " +
- "PathSegment{type=AS_SET, segmentAsNumbers=[4, 5, 6]}]}";
+ "AsPath{pathSegments=[" +
+ "PathSegment{type=AS_CONFED_SEQUENCE, segmentAsNumbers=[1, 2, 3]}, " +
+ "PathSegment{type=AS_CONFED_SET, segmentAsNumbers=[4, 5, 6]}, " +
+ "PathSegment{type=AS_SEQUENCE, segmentAsNumbers=[7, 8, 9]}, " +
+ "PathSegment{type=AS_SET, segmentAsNumbers=[10, 11, 12]}]}";
assertThat(asPath.toString(), is(expectedString));
}
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/constraint/BooleanConstraint.java b/core/api/src/main/java/org/onlab/onos/net/intent/constraint/BooleanConstraint.java
index 97c5695..4e9fc9a 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/constraint/BooleanConstraint.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/constraint/BooleanConstraint.java
@@ -52,7 +52,7 @@
@Override
public boolean validate(Path path, LinkResourceService resourceService) {
for (Link link : path.links()) {
- if (isValid(link, resourceService)) {
+ if (!isValid(link, resourceService)) {
return false;
}
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/constraint/LatencyConstraint.java b/core/api/src/main/java/org/onlab/onos/net/intent/constraint/LatencyConstraint.java
index ac9c5e2..fcdf330 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/constraint/LatencyConstraint.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/constraint/LatencyConstraint.java
@@ -43,7 +43,7 @@
this.latency = latency;
}
- public Duration getLatency() {
+ public Duration latency() {
return latency;
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/constraint/ObstacleConstraint.java b/core/api/src/main/java/org/onlab/onos/net/intent/constraint/ObstacleConstraint.java
index 3ed3fae..6d73fc2 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/constraint/ObstacleConstraint.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/constraint/ObstacleConstraint.java
@@ -19,7 +19,6 @@
import com.google.common.collect.ImmutableSet;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.Link;
-import org.onlab.onos.net.Path;
import org.onlab.onos.net.resource.LinkResourceService;
import java.util.Objects;
@@ -49,17 +48,6 @@
}
@Override
- public boolean validate(Path path, LinkResourceService resourceService) {
- for (Link link : path.links()) {
- if (!isValid(link, resourceService)) {
- return false;
- }
- }
-
- return true;
- }
-
- @Override
public int hashCode() {
return Objects.hash(obstacles);
}
diff --git a/core/api/src/test/java/org/onlab/onos/net/NetTestTools.java b/core/api/src/test/java/org/onlab/onos/net/NetTestTools.java
index 8d2a61b..5f8920b 100644
--- a/core/api/src/test/java/org/onlab/onos/net/NetTestTools.java
+++ b/core/api/src/test/java/org/onlab/onos/net/NetTestTools.java
@@ -15,6 +15,8 @@
*/
package org.onlab.onos.net;
+import org.onlab.onos.TestApplicationId;
+import org.onlab.onos.core.ApplicationId;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.packet.ChassisId;
import org.onlab.packet.IpAddress;
@@ -39,6 +41,7 @@
}
public static final ProviderId PID = new ProviderId("of", "foo");
+ public static final ApplicationId APP_ID = new TestApplicationId("foo");
// Short-hand for producing a device id from a string
public static DeviceId did(String id) {
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 8a31900..d66e849 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
@@ -15,6 +15,7 @@
*/
package org.onlab.onos.net.intent.impl;
+import com.google.common.collect.ImmutableList;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
@@ -30,6 +31,7 @@
import org.onlab.onos.net.topology.PathService;
import org.onlab.onos.net.topology.TopologyEdge;
+import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
@@ -113,12 +115,16 @@
* @param constraints path constraints
*/
ConstraintBasedLinkWeight(List<Constraint> constraints) {
- this.constraints = constraints;
+ if (constraints == null) {
+ this.constraints = Collections.emptyList();
+ } else {
+ this.constraints = ImmutableList.copyOf(constraints);
+ }
}
@Override
public double weight(TopologyEdge edge) {
- if (constraints == null || !constraints.iterator().hasNext()) {
+ if (!constraints.iterator().hasNext()) {
return 1.0;
}
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 461f670..9c196a2 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
@@ -67,7 +67,7 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected LinkResourceService resourceService;
- private ApplicationId appId;
+ protected ApplicationId appId;
@Activate
public void activate() {
diff --git a/core/net/src/test/java/org/onlab/onos/net/intent/IntentTestsMocks.java b/core/net/src/test/java/org/onlab/onos/net/intent/IntentTestsMocks.java
index 6331706..e0fe09e 100644
--- a/core/net/src/test/java/org/onlab/onos/net/intent/IntentTestsMocks.java
+++ b/core/net/src/test/java/org/onlab/onos/net/intent/IntentTestsMocks.java
@@ -16,23 +16,39 @@
package org.onlab.onos.net.intent;
import static org.onlab.onos.net.NetTestTools.createPath;
+import static org.onlab.onos.net.NetTestTools.link;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
+import java.util.LinkedList;
import java.util.List;
import java.util.Set;
+import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.ElementId;
+import org.onlab.onos.net.Link;
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.flow.criteria.Criterion;
import org.onlab.onos.net.flow.criteria.Criterion.Type;
import org.onlab.onos.net.flow.instructions.Instruction;
+import org.onlab.onos.net.resource.BandwidthResourceRequest;
+import org.onlab.onos.net.resource.LambdaResourceRequest;
+import org.onlab.onos.net.resource.LinkResourceAllocations;
+import org.onlab.onos.net.resource.LinkResourceRequest;
+import org.onlab.onos.net.resource.LinkResourceService;
+import org.onlab.onos.net.resource.ResourceAllocation;
+import org.onlab.onos.net.resource.ResourceRequest;
+import org.onlab.onos.net.resource.ResourceType;
+import org.onlab.onos.net.topology.DefaultTopologyEdge;
+import org.onlab.onos.net.topology.DefaultTopologyVertex;
import org.onlab.onos.net.topology.LinkWeight;
import org.onlab.onos.net.topology.PathService;
+import org.onlab.onos.net.topology.TopologyVertex;
/**
* Common mocks used by the intent framework tests.
@@ -101,7 +117,158 @@
@Override
public Set<Path> getPaths(ElementId src, ElementId dst, LinkWeight weight) {
- return getPaths(src, dst);
+ final Set<Path> paths = getPaths(src, dst);
+
+ for (Path path : paths) {
+ final DeviceId srcDevice = path.src().deviceId();
+ final DeviceId dstDevice = path.dst().deviceId();
+ final TopologyVertex srcVertex = new DefaultTopologyVertex(srcDevice);
+ final TopologyVertex dstVertex = new DefaultTopologyVertex(dstDevice);
+ final Link link = link(src.toString(), 1, dst.toString(), 1);
+
+ final double weightValue = weight.weight(new DefaultTopologyEdge(srcVertex, dstVertex, link));
+ if (weightValue < 0) {
+ return new HashSet<>();
+ }
+ }
+ return paths;
}
}
+
+ public static class MockLinkResourceAllocations implements LinkResourceAllocations {
+ @Override
+ public Set<ResourceAllocation> getResourceAllocation(Link link) {
+ return null;
+ }
+
+ @Override
+ public IntentId intendId() {
+ return null;
+ }
+
+ @Override
+ public Collection<Link> links() {
+ return null;
+ }
+
+ @Override
+ public Set<ResourceRequest> resources() {
+ return null;
+ }
+
+ @Override
+ public ResourceType type() {
+ return null;
+ }
+ }
+
+ public static class MockedAllocationFailure extends RuntimeException { }
+
+ public static class MockResourceService implements LinkResourceService {
+
+ double availableBandwidth = -1.0;
+ int availableLambda = -1;
+
+ /**
+ * Allocates a resource service that will allow bandwidth allocations
+ * up to a limit.
+ *
+ * @param bandwidth available bandwidth limit
+ * @return resource manager for bandwidth requests
+ */
+ public static MockResourceService makeBandwidthResourceService(double bandwidth) {
+ final MockResourceService result = new MockResourceService();
+ result.availableBandwidth = bandwidth;
+ return result;
+ }
+
+ /**
+ * Allocates a resource service that will allow lambda allocations.
+ *
+ * @param lambda Lambda to return for allocation requests. Currently unused
+ * @return resource manager for lambda requests
+ */
+ public static MockResourceService makeLambdaResourceService(int lambda) {
+ final MockResourceService result = new MockResourceService();
+ result.availableLambda = lambda;
+ return result;
+ }
+
+ public void setAvailableBandwidth(double availableBandwidth) {
+ this.availableBandwidth = availableBandwidth;
+ }
+
+ public void setAvailableLambda(int availableLambda) {
+ this.availableLambda = availableLambda;
+ }
+
+
+ @Override
+ public LinkResourceAllocations requestResources(LinkResourceRequest req) {
+ int lambda = -1;
+ double bandwidth = -1.0;
+
+ for (ResourceRequest resourceRequest : req.resources()) {
+ if (resourceRequest.type() == ResourceType.BANDWIDTH) {
+ final BandwidthResourceRequest brr = (BandwidthResourceRequest) resourceRequest;
+ bandwidth = brr.bandwidth().toDouble();
+ } else if (resourceRequest.type() == ResourceType.LAMBDA) {
+ lambda = 1;
+ }
+ }
+
+ if (availableBandwidth < bandwidth) {
+ throw new MockedAllocationFailure();
+ }
+ if (lambda > 0 && availableLambda == 0) {
+ throw new MockedAllocationFailure();
+ }
+
+ return new IntentTestsMocks.MockLinkResourceAllocations();
+ }
+
+ @Override
+ public void releaseResources(LinkResourceAllocations allocations) {
+ // Mock
+ }
+
+ @Override
+ public LinkResourceAllocations updateResources(LinkResourceRequest req,
+ LinkResourceAllocations oldAllocations) {
+ return null;
+ }
+
+ @Override
+ public Iterable<LinkResourceAllocations> getAllocations() {
+ return null;
+ }
+
+ @Override
+ public Iterable<LinkResourceAllocations> getAllocations(Link link) {
+ return null;
+ }
+
+ @Override
+ public LinkResourceAllocations getAllocations(IntentId intentId) {
+ return null;
+ }
+
+ @Override
+ public Iterable<ResourceRequest> getAvailableResources(Link link) {
+ final List<ResourceRequest> result = new LinkedList<>();
+ if (availableBandwidth > 0.0) {
+ result.add(new BandwidthResourceRequest(availableBandwidth));
+ }
+ if (availableLambda > 0) {
+ result.add(new LambdaResourceRequest());
+ }
+ return result;
+ }
+
+ @Override
+ public ResourceRequest getAvailableResources(Link link, LinkResourceAllocations allocations) {
+ return null;
+ }
+ }
+
}
diff --git a/core/net/src/test/java/org/onlab/onos/net/intent/impl/PathConstraintCalculationTest.java b/core/net/src/test/java/org/onlab/onos/net/intent/impl/PathConstraintCalculationTest.java
new file mode 100644
index 0000000..3e8e7c1
--- /dev/null
+++ b/core/net/src/test/java/org/onlab/onos/net/intent/impl/PathConstraintCalculationTest.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed 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.impl;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.junit.Test;
+import org.onlab.onos.net.flow.FlowRuleBatchOperation;
+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.IntentTestsMocks;
+import org.onlab.onos.net.intent.PathIntent;
+import org.onlab.onos.net.intent.PointToPointIntent;
+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.onos.net.resource.Lambda;
+import org.onlab.onos.net.resource.LinkResourceService;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.fail;
+import static org.onlab.onos.net.NetTestTools.APP_ID;
+import static org.onlab.onos.net.NetTestTools.connectPoint;
+
+/**
+ * Unit tests for calculating paths for intents with constraints.
+ */
+
+public class PathConstraintCalculationTest {
+
+ /**
+ * Creates a point to point intent compiler for a three switch linear
+ * topology.
+ *
+ * @param resourceService service to use for resource allocation requests
+ * @return point to point compiler
+ */
+ private PointToPointIntentCompiler makeCompiler(LinkResourceService resourceService) {
+ final String[] hops = {"s1", "s2", "s3"};
+ final PointToPointIntentCompiler compiler = new PointToPointIntentCompiler();
+ compiler.resourceService = resourceService;
+ compiler.pathService = new IntentTestsMocks.MockPathService(hops);
+ return compiler;
+ }
+
+ /**
+ * Creates an intent with a given constraint and compiles it. The compiler
+ * will throw PathNotFoundException if the allocations cannot be satisfied.
+ *
+ * @param constraint constraint to apply to the created intent
+ * @param resourceService service to use for resource allocation requests
+ * @return List of compiled intents
+ */
+ private List<Intent> compileIntent(Constraint constraint,
+ LinkResourceService resourceService) {
+ final List<Constraint> constraints = new LinkedList<>();
+ constraints.add(constraint);
+ final TrafficSelector selector = new IntentTestsMocks.MockSelector();
+ final TrafficTreatment treatment = new IntentTestsMocks.MockTreatment();
+
+ final PointToPointIntent intent =
+ new PointToPointIntent(APP_ID,
+ selector,
+ treatment,
+ connectPoint("s1", 1),
+ connectPoint("s3", 1),
+ constraints);
+ final PointToPointIntentCompiler compiler = makeCompiler(resourceService);
+
+ return compiler.compile(intent);
+ }
+
+ /**
+ * Installs a compiled path intent and returns the flow rules it generates.
+ *
+ * @param compiledIntents list of compiled intents
+ * @param resourceService service to use for resource allocation requests
+ * @return
+ */
+ private List<FlowRuleBatchOperation> installIntents(List<Intent> compiledIntents,
+ LinkResourceService resourceService) {
+ final PathIntent path = (PathIntent) compiledIntents.get(0);
+
+ final PathIntentInstaller installer = new PathIntentInstaller();
+ installer.resourceService = resourceService;
+ installer.appId = APP_ID;
+ return installer.install(path);
+ }
+
+ /**
+ * Tests that requests with sufficient available bandwidth succeed.
+ */
+ @Test
+ public void testBandwidthConstrainedIntentSuccess() {
+
+ final LinkResourceService resourceService =
+ IntentTestsMocks.MockResourceService.makeBandwidthResourceService(1000.0);
+ final Constraint constraint = new BandwidthConstraint(Bandwidth.valueOf(100.0));
+
+ final List<Intent> compiledIntents = compileIntent(constraint, resourceService);
+ assertThat(compiledIntents, notNullValue());
+ assertThat(compiledIntents, hasSize(1));
+ }
+
+ /**
+ * Tests that requests with insufficient available bandwidth fail.
+ */
+ @Test
+ public void testBandwidthConstrainedIntentFailure() {
+
+ final LinkResourceService resourceService =
+ IntentTestsMocks.MockResourceService.makeBandwidthResourceService(10.0);
+ final Constraint constraint = new BandwidthConstraint(Bandwidth.valueOf(100.0));
+
+ try {
+ compileIntent(constraint, resourceService);
+ fail("Point to Point compilation with insufficient bandwidth does "
+ + "not throw exception.");
+ } catch (PathNotFoundException noPath) {
+ assertThat(noPath.getMessage(), containsString("No packet path"));
+ }
+ }
+
+ /**
+ * Tests that requests for available lambdas are successful.
+ */
+ @Test
+ public void testLambdaConstrainedIntentSuccess() {
+
+ final Constraint constraint = new LambdaConstraint(Lambda.valueOf(1));
+ final LinkResourceService resourceService =
+ IntentTestsMocks.MockResourceService.makeLambdaResourceService(1);
+
+ final List<Intent> compiledIntents =
+ compileIntent(constraint, resourceService);
+ assertThat(compiledIntents, notNullValue());
+ assertThat(compiledIntents, hasSize(1));
+ }
+
+ /**
+ * Tests that requests for lambdas when there are no available lambdas
+ * fail.
+ */
+ @Test
+ public void testLambdaConstrainedIntentFailure() {
+
+ final Constraint constraint = new LambdaConstraint(Lambda.valueOf(1));
+ final LinkResourceService resourceService =
+ IntentTestsMocks.MockResourceService.makeBandwidthResourceService(10.0);
+ try {
+ compileIntent(constraint, resourceService);
+ fail("Point to Point compilation with no available lambda does "
+ + "not throw exception.");
+ } catch (PathNotFoundException noPath) {
+ assertThat(noPath.getMessage(), containsString("No packet path"));
+ }
+ }
+
+ /**
+ * Tests that installation of bandwidth constrained path intents are
+ * successful.
+ */
+ @Test
+ public void testInstallBandwidthConstrainedIntentSuccess() {
+
+ final IntentTestsMocks.MockResourceService resourceService =
+ IntentTestsMocks.MockResourceService.makeBandwidthResourceService(1000.0);
+ final Constraint constraint = new BandwidthConstraint(Bandwidth.valueOf(100.0));
+
+ final List<Intent> compiledIntents = compileIntent(constraint, resourceService);
+ assertThat(compiledIntents, notNullValue());
+ assertThat(compiledIntents, hasSize(1));
+
+ final List<FlowRuleBatchOperation> flowOperations =
+ installIntents(compiledIntents, resourceService);
+
+ assertThat(flowOperations, notNullValue());
+ assertThat(flowOperations, hasSize(1));
+ }
+
+ /**
+ * Tests that installation of bandwidth constrained path intents fail
+ * if there are no available resources.
+ */
+ @Test
+ public void testInstallBandwidthConstrainedIntentFailure() {
+
+ final IntentTestsMocks.MockResourceService resourceService =
+ IntentTestsMocks.MockResourceService.makeBandwidthResourceService(1000.0);
+ final Constraint constraint = new BandwidthConstraint(Bandwidth.valueOf(100.0));
+
+ final List<Intent> compiledIntents = compileIntent(constraint, resourceService);
+ assertThat(compiledIntents, notNullValue());
+ assertThat(compiledIntents, hasSize(1));
+
+ // Make it look like the available bandwidth was consumed
+ resourceService.setAvailableBandwidth(1.0);
+
+ try {
+ installIntents(compiledIntents, resourceService);
+ fail("Bandwidth request with no available bandwidth did not fail.");
+ } catch (IntentTestsMocks.MockedAllocationFailure failure) {
+ assertThat(failure,
+ instanceOf(IntentTestsMocks.MockedAllocationFailure.class));
+ }
+ }
+
+ /**
+ * Tests that installation of lambda constrained path intents are
+ * successful.
+ */
+ @Test
+ public void testInstallLambdaConstrainedIntentSuccess() {
+
+ final IntentTestsMocks.MockResourceService resourceService =
+ IntentTestsMocks.MockResourceService.makeLambdaResourceService(1);
+ final Constraint constraint = new LambdaConstraint(Lambda.valueOf(1));
+
+ final List<Intent> compiledIntents = compileIntent(constraint, resourceService);
+ assertThat(compiledIntents, notNullValue());
+ assertThat(compiledIntents, hasSize(1));
+
+ final List<FlowRuleBatchOperation> flowOperations =
+ installIntents(compiledIntents, resourceService);
+
+ assertThat(flowOperations, notNullValue());
+ assertThat(flowOperations, hasSize(1));
+ }
+
+ /**
+ * Tests that installation of lambda constrained path intents fail
+ * if there are no available resources.
+ */
+ @Test
+ public void testInstallLambdaConstrainedIntentFailure() {
+
+ final IntentTestsMocks.MockResourceService resourceService =
+ IntentTestsMocks.MockResourceService.makeLambdaResourceService(1);
+ final Constraint constraint = new LambdaConstraint(Lambda.valueOf(1));
+
+ final List<Intent> compiledIntents = compileIntent(constraint, resourceService);
+ assertThat(compiledIntents, notNullValue());
+ assertThat(compiledIntents, hasSize(1));
+
+ // Make it look like the available lambda was consumed
+ resourceService.setAvailableLambda(0);
+
+ try {
+ installIntents(compiledIntents, resourceService);
+ fail("Lambda request with no available lambda did not fail.");
+ } catch (IntentTestsMocks.MockedAllocationFailure failure) {
+ assertThat(failure,
+ instanceOf(IntentTestsMocks.MockedAllocationFailure.class));
+ }
+ }
+
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/mastership/impl/DistributedMastershipStore.java b/core/store/dist/src/main/java/org/onlab/onos/store/mastership/impl/DistributedMastershipStore.java
index 6c2ad6a..dd5041a 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/mastership/impl/DistributedMastershipStore.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/mastership/impl/DistributedMastershipStore.java
@@ -47,7 +47,6 @@
import com.google.common.base.Objects;
import com.hazelcast.core.EntryEvent;
import com.hazelcast.core.EntryListener;
-import com.hazelcast.core.IAtomicLong;
import com.hazelcast.core.MapEvent;
import static org.onlab.onos.net.MastershipRole.*;
@@ -59,8 +58,8 @@
@Component(immediate = true)
@Service
public class DistributedMastershipStore
-extends AbstractHazelcastStore<MastershipEvent, MastershipStoreDelegate>
-implements MastershipStore {
+ extends AbstractHazelcastStore<MastershipEvent, MastershipStoreDelegate>
+ implements MastershipStore {
//term number representing that master has never been chosen yet
private static final Integer NOTHING = 0;
@@ -71,9 +70,6 @@
protected SMap<DeviceId, RoleValue> roleMap;
//devices to terms
protected SMap<DeviceId, Integer> terms;
- //last-known cluster size, used for tie-breaking when partitioning occurs
- protected IAtomicLong clusterSize;
-
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ClusterService clusterService;
@@ -98,7 +94,6 @@
roleMap = new SMap<>(theInstance.<byte[], byte[]>getMap("nodeRoles"), this.serializer);
roleMap.addEntryListener((new RemoteMasterShipEventHandler()), true);
terms = new SMap<>(theInstance.<byte[], byte[]>getMap("terms"), this.serializer);
- clusterSize = theInstance.getAtomicLong("clustersize");
log.info("Started");
}
diff --git a/providers/lldp/src/main/java/org/onlab/onos/provider/lldp/impl/LinkDiscovery.java b/providers/lldp/src/main/java/org/onlab/onos/provider/lldp/impl/LinkDiscovery.java
index db52454..af600ac 100644
--- a/providers/lldp/src/main/java/org/onlab/onos/provider/lldp/impl/LinkDiscovery.java
+++ b/providers/lldp/src/main/java/org/onlab/onos/provider/lldp/impl/LinkDiscovery.java
@@ -241,6 +241,9 @@
*/
@Override
public void run(final Timeout t) {
+ if (isStopped()) {
+ return;
+ }
boolean isMaster = mastershipService.getLocalRole(device.id()) == MASTER;
if (!isMaster) {
if (!isStopped()) {
diff --git a/web/gui/src/main/webapp/index2.html b/web/gui/src/main/webapp/index2.html
index 66050c6..03272e9 100644
--- a/web/gui/src/main/webapp/index2.html
+++ b/web/gui/src/main/webapp/index2.html
@@ -44,6 +44,7 @@
<!-- This is where contributed stylesheets get INJECTED -->
<!-- TODO: replace with template marker and inject refs server-side -->
<link rel="stylesheet" href="topo2.css">
+ <link rel="stylesheet" href="webSockTrace.css">
<!-- General library modules included here-->
@@ -97,6 +98,7 @@
<!-- Contributed (application) views injected here -->
<!-- TODO: replace with template marker and inject refs server-side -->
+ <script src="webSockTrace.js"></script>
<script src="topo2.js"></script>
<!-- finally, build the UI-->
diff --git a/web/gui/src/main/webapp/json/ev/_capture/rx/addDevice_ex1.json b/web/gui/src/main/webapp/json/ev/_capture/rx/addDevice_ex1.json
new file mode 100644
index 0000000..f00cf2c
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/_capture/rx/addDevice_ex1.json
@@ -0,0 +1,15 @@
+{
+ "event": "addDevice",
+ "payload": {
+ "id": "of:0000000000000003",
+ "type": "switch",
+ "online": true,
+ "labels": [
+ "of:0000000000000003",
+ "3",
+ "",
+ null
+ ],
+ "props": {}
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/_capture/rx/addHost_ex1.json b/web/gui/src/main/webapp/json/ev/_capture/rx/addHost_ex1.json
new file mode 100644
index 0000000..a97d15a
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/_capture/rx/addHost_ex1.json
@@ -0,0 +1,17 @@
+{
+ "event": "addHost",
+ "payload": {
+ "id": "6A:40:24:F7:9C:2C/-1",
+ "ingress": "6A:40:24:F7:9C:2C/-1/0-of:0000000000000003/2",
+ "egress": "of:0000000000000003/2-6A:40:24:F7:9C:2C/-1/0",
+ "cp": {
+ "device": "of:0000000000000003",
+ "port": 2
+ },
+ "labels": [
+ "unknown",
+ "6A:40:24:F7:9C:2C"
+ ],
+ "props": {}
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/_capture/rx/addLink_ex1.json b/web/gui/src/main/webapp/json/ev/_capture/rx/addLink_ex1.json
new file mode 100644
index 0000000..92c7848
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/_capture/rx/addLink_ex1.json
@@ -0,0 +1,12 @@
+{
+ "event": "addLink",
+ "payload": {
+ "id": "of:0000000000000007/4-of:0000000000000006/1",
+ "type": "direct",
+ "linkWidth": 2,
+ "src": "of:0000000000000007",
+ "srcPort": "4",
+ "dst": "of:0000000000000006",
+ "dstPort": "1"
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/_capture/rx/removeDevice_fab.json b/web/gui/src/main/webapp/json/ev/_capture/rx/removeDevice_fab.json
new file mode 100644
index 0000000..0e8d47a
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/_capture/rx/removeDevice_fab.json
@@ -0,0 +1,20 @@
+{
+ "__comments__": [
+ "fabricated event",
+ "not sure if this is the actual format",
+ "but we really only care about 'id' being in the payload"
+ ],
+ "event": "removeDevice",
+ "payload": {
+ "id": "of:0000000000000002",
+ "type": "switch",
+ "online": true,
+ "labels": [
+ "of:0000000000000002",
+ "2",
+ "",
+ null
+ ],
+ "props": {}
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/_capture/rx/removeHost_fab.json b/web/gui/src/main/webapp/json/ev/_capture/rx/removeHost_fab.json
new file mode 100644
index 0000000..4237199
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/_capture/rx/removeHost_fab.json
@@ -0,0 +1,22 @@
+{
+ "__comments__": [
+ "fabricated event",
+ "not sure if this is the actual format",
+ "but we really only care about 'id' being in the payload"
+ ],
+ "event": "removeHost",
+ "payload": {
+ "id": "6A:40:24:F7:9C:2C/-1",
+ "ingress": "6A:40:24:F7:9C:2C/-1/0-of:0000000000000003/2",
+ "egress": "of:0000000000000003/2-6A:40:24:F7:9C:2C/-1/0",
+ "cp": {
+ "device": "of:0000000000000003",
+ "port": 2
+ },
+ "labels": [
+ "unknown",
+ "6A:40:24:F7:9C:2C"
+ ],
+ "props": {}
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/_capture/rx/removeLink_ex1.json b/web/gui/src/main/webapp/json/ev/_capture/rx/removeLink_ex1.json
new file mode 100644
index 0000000..8d1dd03
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/_capture/rx/removeLink_ex1.json
@@ -0,0 +1,12 @@
+{
+ "event": "removeLink",
+ "payload": {
+ "id": "of:0000000000000001/1-of:0000000000000002/4",
+ "type": "direct",
+ "linkWidth": 2,
+ "src": "of:0000000000000001",
+ "srcPort": "1",
+ "dst": "of:0000000000000002",
+ "dstPort": "4"
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/_capture/rx/showPath_ex1.json b/web/gui/src/main/webapp/json/ev/_capture/rx/showPath_ex1.json
new file mode 100644
index 0000000..de1023e
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/_capture/rx/showPath_ex1.json
@@ -0,0 +1,15 @@
+{
+ "event": "showPath",
+ "sid": 15,
+ "payload": {
+ "links": [
+ "62:4F:65:BF:FF:B3/-1/0-of:000000000000000b/1",
+ "of:000000000000000b/4-of:000000000000000a/1",
+ "of:000000000000000a/4-of:0000000000000001/3",
+ "of:0000000000000001/1-of:0000000000000002/4",
+ "of:0000000000000002/1-of:0000000000000003/4",
+ "of:0000000000000003/1-CA:4B:EE:A4:B0:33/-1/0"
+ ],
+ "intentId": "0x52a914f9"
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/_capture/rx/updateDevice_ex1.json b/web/gui/src/main/webapp/json/ev/_capture/rx/updateDevice_ex1.json
new file mode 100644
index 0000000..dda6186
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/_capture/rx/updateDevice_ex1.json
@@ -0,0 +1,15 @@
+{
+ "event": "updateDevice",
+ "payload": {
+ "id": "of:0000000000000002",
+ "type": "switch",
+ "online": true,
+ "labels": [
+ "of:0000000000000002",
+ "2",
+ "",
+ null
+ ],
+ "props": {}
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/_capture/rx/updateDevice_ex2.json b/web/gui/src/main/webapp/json/ev/_capture/rx/updateDevice_ex2.json
new file mode 100644
index 0000000..d607f98
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/_capture/rx/updateDevice_ex2.json
@@ -0,0 +1,15 @@
+{
+ "event": "updateDevice",
+ "payload": {
+ "id": "of:0000000000000002",
+ "type": "switch",
+ "online": false,
+ "labels": [
+ "of:0000000000000002",
+ "2",
+ "",
+ null
+ ],
+ "props": {}
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/_capture/rx/updateHost.json b/web/gui/src/main/webapp/json/ev/_capture/rx/updateHost.json
new file mode 100644
index 0000000..fd7361c
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/_capture/rx/updateHost.json
@@ -0,0 +1,17 @@
+{
+ "event": "updateHost",
+ "payload": {
+ "id": "AA:C2:74:3F:B8:06/-1",
+ "ingress": "AA:C2:74:3F:B8:06/-1/0-of:0000000000000005/3",
+ "egress": "of:0000000000000005/3-AA:C2:74:3F:B8:06/-1/0",
+ "cp": {
+ "device": "of:0000000000000005",
+ "port": 3
+ },
+ "labels": [
+ "10.0.0.9",
+ "AA:C2:74:3F:B8:06"
+ ],
+ "props":{}
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/_capture/rx/updateLink_ex1.json b/web/gui/src/main/webapp/json/ev/_capture/rx/updateLink_ex1.json
new file mode 100644
index 0000000..3be5c5f
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/_capture/rx/updateLink_ex1.json
@@ -0,0 +1,12 @@
+{
+ "event": "updateLink",
+ "payload": {
+ "id": "of:0000000000000002/4-of:0000000000000001/1",
+ "type": "direct",
+ "linkWidth": 2,
+ "src": "of:0000000000000002",
+ "srcPort": "4",
+ "dst": "of:0000000000000001",
+ "dstPort": "1"
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/_capture/tx/requestPath_ex1.json b/web/gui/src/main/webapp/json/ev/_capture/tx/requestPath_ex1.json
new file mode 100644
index 0000000..4963865
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/_capture/tx/requestPath_ex1.json
@@ -0,0 +1,8 @@
+{
+ "event": "requestPath",
+ "sid": 15,
+ "payload": {
+ "one": "62:4F:65:BF:FF:B3/-1",
+ "two": "CA:4B:EE:A4:B0:33/-1"
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/_capture/tx/updateMeta_ex1.json b/web/gui/src/main/webapp/json/ev/_capture/tx/updateMeta_ex1.json
new file mode 100644
index 0000000..c04727e
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/_capture/tx/updateMeta_ex1.json
@@ -0,0 +1,10 @@
+{
+ "event": "updateMeta",
+ "sid": 11,
+ "payload": {
+ "id": "62:4F:65:BF:FF:B3/-1",
+ "class": "host",
+ "x": 197,
+ "y": 177
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/intent/ev_1_ui.json b/web/gui/src/main/webapp/json/ev/intentSketch/ev_1_ui.json
similarity index 100%
rename from web/gui/src/main/webapp/json/ev/intent/ev_1_ui.json
rename to web/gui/src/main/webapp/json/ev/intentSketch/ev_1_ui.json
diff --git a/web/gui/src/main/webapp/json/ev/intent/ev_2_onos.json b/web/gui/src/main/webapp/json/ev/intentSketch/ev_2_onos.json
similarity index 100%
rename from web/gui/src/main/webapp/json/ev/intent/ev_2_onos.json
rename to web/gui/src/main/webapp/json/ev/intentSketch/ev_2_onos.json
diff --git a/web/gui/src/main/webapp/json/ev/intent/ev_3_ui.json b/web/gui/src/main/webapp/json/ev/intentSketch/ev_3_ui.json
similarity index 100%
rename from web/gui/src/main/webapp/json/ev/intent/ev_3_ui.json
rename to web/gui/src/main/webapp/json/ev/intentSketch/ev_3_ui.json
diff --git a/web/gui/src/main/webapp/json/ev/intent/ev_4_onos.json b/web/gui/src/main/webapp/json/ev/intentSketch/ev_4_onos.json
similarity index 100%
rename from web/gui/src/main/webapp/json/ev/intent/ev_4_onos.json
rename to web/gui/src/main/webapp/json/ev/intentSketch/ev_4_onos.json
diff --git a/web/gui/src/main/webapp/json/ev/intent/ev_5_onos.json b/web/gui/src/main/webapp/json/ev/intentSketch/ev_5_onos.json
similarity index 100%
rename from web/gui/src/main/webapp/json/ev/intent/ev_5_onos.json
rename to web/gui/src/main/webapp/json/ev/intentSketch/ev_5_onos.json
diff --git a/web/gui/src/main/webapp/json/ev/intent/ev_6_onos.json b/web/gui/src/main/webapp/json/ev/intentSketch/ev_6_onos.json
similarity index 100%
rename from web/gui/src/main/webapp/json/ev/intent/ev_6_onos.json
rename to web/gui/src/main/webapp/json/ev/intentSketch/ev_6_onos.json
diff --git a/web/gui/src/main/webapp/json/ev/intent/ev_7_ui.json b/web/gui/src/main/webapp/json/ev/intentSketch/ev_7_ui.json
similarity index 100%
rename from web/gui/src/main/webapp/json/ev/intent/ev_7_ui.json
rename to web/gui/src/main/webapp/json/ev/intentSketch/ev_7_ui.json
diff --git a/web/gui/src/main/webapp/json/ev/intent/scenario.json b/web/gui/src/main/webapp/json/ev/intentSketch/scenario.json
similarity index 100%
rename from web/gui/src/main/webapp/json/ev/intent/scenario.json
rename to web/gui/src/main/webapp/json/ev/intentSketch/scenario.json
diff --git a/web/gui/src/main/webapp/json/ev/simple/ev_8_ui.json b/web/gui/src/main/webapp/json/ev/simple/ev_10_ui.json
similarity index 100%
rename from web/gui/src/main/webapp/json/ev/simple/ev_8_ui.json
rename to web/gui/src/main/webapp/json/ev/simple/ev_10_ui.json
diff --git a/web/gui/src/main/webapp/json/ev/simple/ev_3_onos.json b/web/gui/src/main/webapp/json/ev/simple/ev_3_onos.json
index ac521c4..73013a4 100644
--- a/web/gui/src/main/webapp/json/ev/simple/ev_3_onos.json
+++ b/web/gui/src/main/webapp/json/ev/simple/ev_3_onos.json
@@ -1,15 +1,18 @@
{
- "event": "addLink",
+ "event": "updateDevice",
"payload": {
- "id": "of:0000ffffffff0003/21-of:0000ffffffff0008/20",
- "type": "direct",
- "linkWidth": 2,
- "src": "of:0000ffffffff0003",
- "srcPort": "21",
- "dst": "of:0000ffffffff0008",
- "dstPort": "20",
- "props" : {
- "BW": "70 G"
+ "id": "of:0000ffffffff0008",
+ "type": "switch",
+ "online": true,
+ "labels": [
+ "0000ffffffff0008",
+ "FF:FF:FF:FF:00:08",
+ "sw-8-yo",
+ ""
+ ],
+ "metaUi": {
+ "x": 400,
+ "y": 280
}
}
}
diff --git a/web/gui/src/main/webapp/json/ev/simple/ev_4_onos.json b/web/gui/src/main/webapp/json/ev/simple/ev_4_onos.json
index 993570b..958af28 100644
--- a/web/gui/src/main/webapp/json/ev/simple/ev_4_onos.json
+++ b/web/gui/src/main/webapp/json/ev/simple/ev_4_onos.json
@@ -1,17 +1,18 @@
{
- "event": "addHost",
+ "event": "updateDevice",
"payload": {
- "id": "0E:2A:69:30:13:86/-1",
- "ingress": "0E:2A:69:30:13:86/-1/0-of:0000ffffffff0003/2",
- "egress": "of:0000ffffffff0003/2-0E:2A:69:30:13:86/-1/0",
- "cp": {
- "device": "of:0000ffffffff0003",
- "port": 2
- },
+ "id": "of:0000ffffffff0003",
+ "type": "switch",
+ "online": true,
"labels": [
- "unknown",
- "0E:2A:69:30:13:86"
+ "0000ffffffff0003",
+ "FF:FF:FF:FF:00:03",
+ "sw-3-yo",
+ ""
],
- "props": {}
+ "metaUi": {
+ "x": 800,
+ "y": 280
+ }
}
}
diff --git a/web/gui/src/main/webapp/json/ev/simple/ev_5_onos.json b/web/gui/src/main/webapp/json/ev/simple/ev_5_onos.json
index 17864a6..ac521c4 100644
--- a/web/gui/src/main/webapp/json/ev/simple/ev_5_onos.json
+++ b/web/gui/src/main/webapp/json/ev/simple/ev_5_onos.json
@@ -1,17 +1,15 @@
{
- "event": "addHost",
+ "event": "addLink",
"payload": {
- "id": "A6:96:E5:03:52:5F/-1",
- "ingress": "A6:96:E5:03:52:5F/-1/0-of:0000ffffffff0008/1",
- "egress": "of:0000ffffffff0008/1-A6:96:E5:03:52:5F/-1/0",
- "cp": {
- "device": "of:0000ffffffff0008",
- "port": 1
- },
- "labels": [
- "unknown",
- "A6:96:E5:03:52:5F"
- ],
- "props": {}
+ "id": "of:0000ffffffff0003/21-of:0000ffffffff0008/20",
+ "type": "direct",
+ "linkWidth": 2,
+ "src": "of:0000ffffffff0003",
+ "srcPort": "21",
+ "dst": "of:0000ffffffff0008",
+ "dstPort": "20",
+ "props" : {
+ "BW": "70 G"
+ }
}
}
diff --git a/web/gui/src/main/webapp/json/ev/simple/ev_6_onos.json b/web/gui/src/main/webapp/json/ev/simple/ev_6_onos.json
index 3a3ea9e..993570b 100644
--- a/web/gui/src/main/webapp/json/ev/simple/ev_6_onos.json
+++ b/web/gui/src/main/webapp/json/ev/simple/ev_6_onos.json
@@ -1,5 +1,5 @@
{
- "event": "updateHost",
+ "event": "addHost",
"payload": {
"id": "0E:2A:69:30:13:86/-1",
"ingress": "0E:2A:69:30:13:86/-1/0-of:0000ffffffff0003/2",
@@ -9,7 +9,7 @@
"port": 2
},
"labels": [
- "10.0.0.13",
+ "unknown",
"0E:2A:69:30:13:86"
],
"props": {}
diff --git a/web/gui/src/main/webapp/json/ev/simple/ev_7_onos.json b/web/gui/src/main/webapp/json/ev/simple/ev_7_onos.json
index 0fb56fa..17864a6 100644
--- a/web/gui/src/main/webapp/json/ev/simple/ev_7_onos.json
+++ b/web/gui/src/main/webapp/json/ev/simple/ev_7_onos.json
@@ -1,5 +1,5 @@
{
- "event": "updateHost",
+ "event": "addHost",
"payload": {
"id": "A6:96:E5:03:52:5F/-1",
"ingress": "A6:96:E5:03:52:5F/-1/0-of:0000ffffffff0008/1",
@@ -9,7 +9,7 @@
"port": 1
},
"labels": [
- "10.0.0.17",
+ "unknown",
"A6:96:E5:03:52:5F"
],
"props": {}
diff --git a/web/gui/src/main/webapp/json/ev/simple/ev_8_onos.json b/web/gui/src/main/webapp/json/ev/simple/ev_8_onos.json
new file mode 100644
index 0000000..3a3ea9e
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/simple/ev_8_onos.json
@@ -0,0 +1,17 @@
+{
+ "event": "updateHost",
+ "payload": {
+ "id": "0E:2A:69:30:13:86/-1",
+ "ingress": "0E:2A:69:30:13:86/-1/0-of:0000ffffffff0003/2",
+ "egress": "of:0000ffffffff0003/2-0E:2A:69:30:13:86/-1/0",
+ "cp": {
+ "device": "of:0000ffffffff0003",
+ "port": 2
+ },
+ "labels": [
+ "10.0.0.13",
+ "0E:2A:69:30:13:86"
+ ],
+ "props": {}
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/simple/ev_9_onos.json b/web/gui/src/main/webapp/json/ev/simple/ev_9_onos.json
new file mode 100644
index 0000000..0fb56fa
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/simple/ev_9_onos.json
@@ -0,0 +1,17 @@
+{
+ "event": "updateHost",
+ "payload": {
+ "id": "A6:96:E5:03:52:5F/-1",
+ "ingress": "A6:96:E5:03:52:5F/-1/0-of:0000ffffffff0008/1",
+ "egress": "of:0000ffffffff0008/1-A6:96:E5:03:52:5F/-1/0",
+ "cp": {
+ "device": "of:0000ffffffff0008",
+ "port": 1
+ },
+ "labels": [
+ "10.0.0.17",
+ "A6:96:E5:03:52:5F"
+ ],
+ "props": {}
+ }
+}
diff --git a/web/gui/src/main/webapp/json/ev/simple/scenario.json b/web/gui/src/main/webapp/json/ev/simple/scenario.json
index e320413..19d6190 100644
--- a/web/gui/src/main/webapp/json/ev/simple/scenario.json
+++ b/web/gui/src/main/webapp/json/ev/simple/scenario.json
@@ -6,5 +6,17 @@
"title": "Simple Startup Scenario",
"params": {
"lastAuto": 0
- }
+ },
+ "description": [
+ "1. add device [8] (offline)",
+ "2. add device [3] (offline)",
+ "3. update device [8] (online)",
+ "4. update device [3] (online)",
+ "5. add link [3] --> [8]",
+ "6. add host (to [3])",
+ "7. add host (to [8])",
+ "8. update host[3] (IP now 10.0.0.13)",
+ "9. update host[8] (IP now 10.0.0.17)",
+ ""
+ ]
}
\ No newline at end of file
diff --git a/web/gui/src/main/webapp/onos2.js b/web/gui/src/main/webapp/onos2.js
index a31b20f..f38b35f 100644
--- a/web/gui/src/main/webapp/onos2.js
+++ b/web/gui/src/main/webapp/onos2.js
@@ -33,7 +33,8 @@
var uiApi,
viewApi,
navApi,
- libApi;
+ libApi,
+ exported = {};
var defaultOptions = {
trace: false,
@@ -658,6 +659,7 @@
return makeUid(this, id);
},
+ // TODO : add exportApi and importApi methods
// TODO : implement custom dialogs
// Consider enhancing alert mechanism to handle multiples
@@ -737,6 +739,7 @@
// ..........................................................
// View API
+ // TODO: deprecated
viewApi = {
/** @api view empty( )
* Empties the current view.
@@ -802,7 +805,8 @@
lib: libApi,
//view: viewApi,
nav: navApi,
- buildUi: buildOnosUi
+ buildUi: buildOnosUi,
+ exported: exported
};
};
diff --git a/web/gui/src/main/webapp/topo2.css b/web/gui/src/main/webapp/topo2.css
index acd0bc9..6c0c313 100644
--- a/web/gui/src/main/webapp/topo2.css
+++ b/web/gui/src/main/webapp/topo2.css
@@ -41,11 +41,16 @@
stroke: #ccc;
}
-#topo svg .node.device.switch {
+/* note: device is offline without the 'online' class */
+#topo svg .node.device {
+ fill: #777;
+}
+
+#topo svg .node.device.switch.online {
fill: #17f;
}
-#topo svg .node.device.roadm {
+#topo svg .node.device.roadm.online {
fill: #03c;
}
@@ -53,12 +58,17 @@
fill: #846;
}
+/* note: device is offline without the 'online' class */
#topo svg .node.device text {
- fill: white;
+ fill: #aaa;
font: 10pt sans-serif;
pointer-events: none;
}
+#topo svg .node.device.online text {
+ fill: white;
+}
+
#topo svg .node.host text {
fill: #846;
font: 9pt sans-serif;
diff --git a/web/gui/src/main/webapp/topo2.js b/web/gui/src/main/webapp/topo2.js
index 55e463c..f6a8456 100644
--- a/web/gui/src/main/webapp/topo2.js
+++ b/web/gui/src/main/webapp/topo2.js
@@ -24,7 +24,8 @@
'use strict';
// shorter names for library APIs
- var d3u = onos.lib.d3util;
+ var d3u = onos.lib.d3util,
+ trace;
// configuration data
var config = {
@@ -241,8 +242,8 @@
}
function handleUiEvent(data) {
- testDebug('handleUiEvent(): ' + data.event);
- // TODO:
+ scenario.view.alert('UI Tx: ' + data.event + '\n\n' +
+ JSON.stringify(data));
}
function injectStartupEvents(view) {
@@ -259,32 +260,44 @@
bgImg.style('visibility', (vis === 'hidden') ? 'visible' : 'hidden');
}
+ function updateDeviceLabel(d) {
+ var label = niceLabel(deviceLabel(d)),
+ node = d.el,
+ box;
+
+ node.select('text')
+ .text(label)
+ .style('opacity', 0)
+ .transition()
+ .style('opacity', 1);
+
+ box = adjustRectToFitText(node);
+
+ node.select('rect')
+ .transition()
+ .attr(box);
+
+ node.select('image')
+ .transition()
+ .attr('x', box.x + config.icons.xoff)
+ .attr('y', box.y + config.icons.yoff);
+ }
+
+ function updateHostLabel(d) {
+ var label = hostLabel(d),
+ host = d.el;
+
+ host.select('text').text(label);
+ }
+
function cycleLabels() {
- deviceLabelIndex = (deviceLabelIndex === network.deviceLabelCount - 1) ? 0 : deviceLabelIndex + 1;
+ deviceLabelIndex = (deviceLabelIndex === network.deviceLabelCount - 1)
+ ? 0 : deviceLabelIndex + 1;
network.nodes.forEach(function (d) {
- if (d.class !== 'device') { return; }
-
- var label = niceLabel(deviceLabel(d)),
- node = d.el,
- box;
-
- node.select('text')
- .text(label)
- .style('opacity', 0)
- .transition()
- .style('opacity', 1);
-
- box = adjustRectToFitText(node);
-
- node.select('rect')
- .transition()
- .attr(box);
-
- node.select('image')
- .transition()
- .attr('x', box.x + config.icons.xoff)
- .attr('y', box.y + config.icons.yoff);
+ if (d.class === 'device') {
+ updateDeviceLabel(d);
+ }
});
}
@@ -348,15 +361,20 @@
// ==============================
// Event handlers for server-pushed events
+ function logicError(msg) {
+ // TODO, report logic error to server, via websock, so it can be logged
+ network.view.alert('Logic Error:\n\n' + msg);
+ }
+
var eventDispatch = {
addDevice: addDevice,
- updateDevice: stillToImplement,
- removeDevice: stillToImplement,
addLink: addLink,
- updateLink: stillToImplement,
- removeLink: stillToImplement,
addHost: addHost,
+ updateDevice: updateDevice,
+ updateLink: stillToImplement,
updateHost: updateHost,
+ removeDevice: stillToImplement,
+ removeLink: stillToImplement,
removeHost: stillToImplement,
showPath: showPath
};
@@ -364,8 +382,6 @@
function addDevice(data) {
var device = data.payload,
nodeData = createDeviceNode(device);
- note('addDevice', device.id);
-
network.nodes.push(nodeData);
network.lookup[nodeData.id] = nodeData;
updateNodes();
@@ -375,10 +391,7 @@
function addLink(data) {
var link = data.payload,
lnk = createLink(link);
-
if (lnk) {
- note('addLink', link.id);
-
network.links.push(lnk);
network.lookup[lnk.id] = lnk;
updateLinks();
@@ -390,8 +403,6 @@
var host = data.payload,
node = createHostNode(host),
lnk;
- note('addHost', node.id);
-
network.nodes.push(node);
network.lookup[host.id] = node;
updateNodes();
@@ -406,13 +417,28 @@
network.force.start();
}
+ function updateDevice(data) {
+ var device = data.payload,
+ id = device.id,
+ nodeData = network.lookup[id];
+ if (nodeData) {
+ $.extend(nodeData, device);
+ updateDeviceState(nodeData);
+ } else {
+ logicError('updateDevice lookup fail. ID = "' + id + '"');
+ }
+ }
+
function updateHost(data) {
var host = data.payload,
- hostData = network.lookup[host.id];
- note('updateHost', host.id);
-
- $.extend(hostData, host);
- updateNodes();
+ id = host.id,
+ hostData = network.lookup[id];
+ if (hostData) {
+ $.extend(hostData, host);
+ updateHostState(hostData);
+ } else {
+ logicError('updateHost lookup fail. ID = "' + id + '"');
+ }
}
function showPath(data) {
@@ -466,9 +492,8 @@
lnk;
if (!dstNode) {
- // TODO: send warning message back to server on websocket
- network.view.alert('switch not on map for link\n\n' +
- 'src = ' + src + '\ndst = ' + dst);
+ logicError('switch not on map for link\n\n' +
+ 'src = ' + src + '\ndst = ' + dst);
return null;
}
@@ -500,9 +525,8 @@
dstNode = network.lookup[dst];
if (!(srcNode && dstNode)) {
- // TODO: send warning message back to server on websocket
- network.view.alert('nodes not on map for link\n\n' +
- 'src = ' + src + '\ndst = ' + dst);
+ logicError('nodes not on map for link\n\n' +
+ 'src = ' + src + '\ndst = ' + dst);
return null;
}
@@ -578,11 +602,12 @@
function createDeviceNode(device) {
// start with the object as is
var node = device,
- type = device.type;
+ type = device.type,
+ svgCls = type ? 'node device ' + type : 'node device';
// Augment as needed...
node.class = 'device';
- node.svgClass = type ? 'node device ' + type : 'node device';
+ node.svgClass = device.online ? svgCls + ' online' : svgCls;
positionNode(node);
// cache label array length
@@ -669,15 +694,24 @@
return (label && label.trim()) ? label : '.';
}
+ function updateDeviceState(nodeData) {
+ nodeData.el.classed('online', nodeData.online);
+ updateDeviceLabel(nodeData);
+ // TODO: review what else might need to be updated
+ }
+
+ function updateHostState(hostData) {
+ updateHostLabel(hostData);
+ // TODO: review what else might need to be updated
+ }
+
+
function updateNodes() {
node = nodeG.selectAll('.node')
.data(network.nodes, function (d) { return d.id; });
// operate on existing nodes, if necessary
// update host labels
- node.filter('.host').select('text')
- .text(hostLabel);
-
//node .foo() .bar() ...
// operate on entering nodes:
@@ -828,7 +862,7 @@
webSock.ws.onmessage = function(m) {
if (m.data) {
- console.log(m.data);
+ wsTraceRx(m.data);
handleServerEvent(JSON.parse(m.data));
}
};
@@ -858,11 +892,28 @@
function sendMessage(evType, payload) {
var toSend = {
- event: evType,
- sid: ++sid,
- payload: payload
- };
- webSock.send(JSON.stringify(toSend));
+ event: evType,
+ sid: ++sid,
+ payload: payload
+ },
+ asText = JSON.stringify(toSend);
+ wsTraceTx(asText);
+ webSock.send(asText);
+ }
+
+ function wsTraceTx(msg) {
+ wsTrace('tx', msg);
+ }
+ function wsTraceRx(msg) {
+ wsTrace('rx', msg);
+ }
+ function wsTrace(rxtx, msg) {
+
+ console.log('[' + rxtx + '] ' + msg);
+ // TODO: integrate with trace view
+ //if (trace) {
+ // trace.output(rxtx, msg);
+ //}
}
@@ -944,12 +995,19 @@
sc.evNumber = 0;
d3.json(urlSc, function(err, data) {
- var p = data && data.params || {};
+ var p = data && data.params || {},
+ desc = data && data.description || null,
+ intro;
+
if (err) {
view.alert('No scenario found:\n\n' + urlSc + '\n\n' + err);
} else {
sc.params = p;
- view.alert("Scenario loaded: " + ctx + '\n\n' + data.title);
+ intro = "Scenario loaded: " + ctx + '\n\n' + data.title;
+ if (desc) {
+ intro += '\n\n ' + desc.join('\n ');
+ }
+ view.alert(intro);
}
});
@@ -967,6 +1025,9 @@
fpad = fcfg.pad,
forceDim = [w - 2*fpad, h - 2*fpad];
+ // TODO: set trace api
+ //trace = onos.exported.webSockTrace;
+
// NOTE: view.$div is a D3 selection of the view's div
svg = view.$div.append('svg');
setSize(svg, view);
diff --git a/web/gui/src/main/webapp/webSockTrace.css b/web/gui/src/main/webapp/webSockTrace.css
new file mode 100644
index 0000000..6669124
--- /dev/null
+++ b/web/gui/src/main/webapp/webSockTrace.css
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed 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.
+ */
+
+/*
+ ONOS GUI -- Web Socket Trace -- CSS file
+
+ @author Simon Hunt
+ */
+
+#webSockTrace .toolbar {
+ height: 36px;
+ padding: 4px;
+ vertical-align: baseline;
+ font-size: 12pt;
+ margin-top: 6px;
+}
+
+/* theme-related */
+#webSockTrace .toolbar {
+ background-color: #448;
+ color: #fff;
+}
+
+#webSockTrace .output {
+ overflow-y: scroll;
+}
+
+/* theme-related */
+#webSockTrace .output {
+ background-color: #eef;
+ color: #226;
+}
+
+#webSockTrace .output p {
+ margin: 2px 8px;
+ font-size: 10pt;
+ padding-left: 6px;
+}
+
+/* theme-related */
+#webSockTrace .output p.tx {
+ color: magenta;
+}
+#webSockTrace .output p.rx {
+ color: blue;
+}
+
+
+#webSockTrace .output p.subtitle {
+ margin: 6px 8px;
+ padding-left: 2px;
+ font-size: 12pt;
+ font-weight: bold;
+ font-style: italic;
+}
+
+/* theme-related */
+#webSockTrace .output p.subtitle {
+ color: #626;
+}
diff --git a/web/gui/src/main/webapp/webSockTrace.js b/web/gui/src/main/webapp/webSockTrace.js
new file mode 100644
index 0000000..2f09b31
--- /dev/null
+++ b/web/gui/src/main/webapp/webSockTrace.js
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed 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.
+ */
+
+/*
+ View that traces messages across the websocket.
+
+ @author Simon Hunt
+ */
+
+(function (onos) {
+ 'use strict';
+
+ var v,
+ $d,
+ tb,
+ out,
+ which = 'tx',
+ keyDispatch = {
+ space: function () {
+ output(which, "Simon woz 'ere... " + which);
+ which = (which === 'tx') ? 'rx' : 'tx';
+ }
+ };
+
+
+ function addHeader() {
+ tb = $d.append('div')
+ .attr('class', 'toolbar');
+ tb.append('span').text('Web Socket Trace');
+ }
+
+ function addOutput() {
+ out = $d.append('div')
+ .attr('class', 'output');
+ }
+
+ function subtitle(msg) {
+ out.append('p').attr('class', 'subtitle').text(msg);
+ }
+
+ function output(rxtx, msg) {
+ out.append('p').attr('class', rxtx).text(msg);
+ }
+
+ // invoked only the first time the view is loaded
+ function preload(view, ctx, flags) {
+ // NOTE: view.$div is a D3 selection of the view's div
+ v = view;
+ $d = v.$div;
+ addHeader();
+ addOutput();
+
+
+ // hack for now, to allow topo access to our API
+ // TODO: add 'exportApi' and 'importApi' to views.
+ onos.exported.webSockTrace = {
+ subtitle: subtitle,
+ output: output
+ };
+ }
+
+ // invoked just prior to loading the view
+ function reset(view, ctx, flags) {
+
+ }
+
+ // invoked when the view is loaded
+ function load(view, ctx, flags) {
+ resize(view, ctx, flags);
+ view.setKeys(keyDispatch);
+ subtitle('Waiting for messages...');
+ }
+
+ // invoked when the view is resized
+ function resize(view, ctx, flags) {
+ var h = view.height();
+ out.style('height', h + 'px');
+
+ }
+
+ // == register the view here, with links to lifecycle callbacks
+
+ onos.ui.addView('webSockTrace', {
+ preload: preload,
+ load: load,
+ resize: resize
+ });
+
+}(ONOS));