ONOS-4971: Synthetic Link Data -- WIP
- Breaking out UiLink to subclasses for device links, host links, region links, region-device links,
- (soon, also peer links).
- Augmenting UiLinkId to include regions as endpoints.
- Introduced UiSynthLink to encapsulate synthetic links bound to regions.
- Model Cache now computes synthetic links from the underlying link data.
- Added endPointA/B() and type() methods to UiLink.
- Updated topo2CurrentRegion response to include synth-links for the region.
Change-Id: Ifa62a15fbe0a58b134d92278b201fa7a72cbfa83
diff --git a/core/api/src/main/java/org/onosproject/ui/model/topo/UiDeviceLink.java b/core/api/src/main/java/org/onosproject/ui/model/topo/UiDeviceLink.java
new file mode 100644
index 0000000..4575678
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/ui/model/topo/UiDeviceLink.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2016-present 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.onosproject.ui.model.topo;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.PortNumber;
+
+/**
+ * Represents a link between two devices; that is, an infrastructure link.
+ */
+public class UiDeviceLink extends UiLink {
+
+ private static final String E_UNASSOC =
+ "backing link not associated with this UI device link: ";
+
+ // devices and ports at either end of this link
+ private DeviceId deviceA;
+ private DeviceId deviceB;
+ private PortNumber portA;
+ private PortNumber portB;
+
+ // two unidirectional links underlying this link...
+ private Link linkAtoB;
+ private Link linkBtoA;
+
+
+ /**
+ * Creates a device to device UI link.
+ *
+ * @param topology parent topology
+ * @param id canonicalized link identifier
+ */
+ public UiDeviceLink(UiTopology topology, UiLinkId id) {
+ super(topology, id);
+ }
+
+ @Override
+ public String endPointA() {
+ return deviceA + UiLinkId.ID_PORT_DELIMITER + portA;
+ }
+
+ @Override
+ public String endPointB() {
+ return deviceB + UiLinkId.ID_PORT_DELIMITER + portB;
+ }
+
+
+ @Override
+ protected void destroy() {
+ deviceA = null;
+ deviceB = null;
+ portA = null;
+ portB = null;
+ linkAtoB = null;
+ linkBtoA = null;
+ }
+
+
+ /**
+ * Attaches the given backing link to this UI link. This method will
+ * throw an exception if this UI link is not representative of the
+ * supplied link.
+ *
+ * @param link backing link to attach
+ * @throws IllegalArgumentException if the link is not appropriate
+ */
+ public void attachBackingLink(Link link) {
+ UiLinkId.Direction d = id.directionOf(link);
+
+ if (d == UiLinkId.Direction.A_TO_B) {
+ linkAtoB = link;
+ deviceA = link.src().deviceId();
+ portA = link.src().port();
+ deviceB = link.dst().deviceId();
+ portB = link.dst().port();
+
+ } else if (d == UiLinkId.Direction.B_TO_A) {
+ linkBtoA = link;
+ deviceB = link.src().deviceId();
+ portB = link.src().port();
+ deviceA = link.dst().deviceId();
+ portA = link.dst().port();
+
+ } else {
+ throw new IllegalArgumentException(E_UNASSOC + link);
+ }
+ }
+
+ /**
+ * Detaches the given backing link from this UI link, returning true if the
+ * reverse link is still attached, or false otherwise.
+ *
+ * @param link the backing link to detach
+ * @return true if other link still attached, false otherwise
+ * @throws IllegalArgumentException if the link is not appropriate
+ */
+ public boolean detachBackingLink(Link link) {
+ UiLinkId.Direction d = id.directionOf(link);
+ if (d == UiLinkId.Direction.A_TO_B) {
+ linkAtoB = null;
+ return linkBtoA != null;
+ }
+ if (d == UiLinkId.Direction.B_TO_A) {
+ linkBtoA = null;
+ return linkAtoB != null;
+ }
+ throw new IllegalArgumentException(E_UNASSOC + link);
+ }
+
+
+ /**
+ * Returns the identity of device A.
+ *
+ * @return device A ID
+ */
+ public DeviceId deviceA() {
+ return deviceA;
+ }
+
+ /**
+ * Returns the port number of device A.
+ *
+ * @return port A
+ */
+ public PortNumber portA() {
+ return portA;
+ }
+
+ /**
+ * Returns the identity of device B.
+ *
+ * @return device B ID
+ */
+ public DeviceId deviceB() {
+ return deviceB;
+ }
+
+ /**
+ * Returns the port number of device B.
+ *
+ * @return port B
+ */
+ public PortNumber portB() {
+ return portB;
+ }
+
+ /**
+ * Returns backing link from A to B.
+ *
+ * @return backing link A to B
+ */
+ public Link linkAtoB() {
+ return linkAtoB;
+ }
+
+ /**
+ * Returns backing link from B to A.
+ *
+ * @return backing link B to A
+ */
+ public Link linkBtoA() {
+ return linkBtoA;
+ }
+
+}
diff --git a/core/api/src/main/java/org/onosproject/ui/model/topo/UiEdgeLink.java b/core/api/src/main/java/org/onosproject/ui/model/topo/UiEdgeLink.java
new file mode 100644
index 0000000..909ef1a
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/ui/model/topo/UiEdgeLink.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2016-present 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.onosproject.ui.model.topo;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.EdgeLink;
+import org.onosproject.net.PortNumber;
+
+/**
+ * Designates a link between a device and a host; that is, an edge link.
+ */
+public class UiEdgeLink extends UiLink {
+
+ private static final String E_UNASSOC =
+ "backing link not associated with this UI edge link: ";
+
+ // private (synthetic) host link
+ private DeviceId edgeDevice;
+ private PortNumber edgePort;
+ private EdgeLink edgeLink;
+
+ /**
+ * Creates a UI link.
+ *
+ * @param topology parent topology
+ * @param id canonicalized link identifier
+ */
+ public UiEdgeLink(UiTopology topology, UiLinkId id) {
+ super(topology, id);
+ }
+
+ @Override
+ public String endPointA() {
+ return edgeLink.hostId().toString();
+ }
+
+ @Override
+ public String endPointB() {
+ return edgeDevice + UiLinkId.ID_PORT_DELIMITER + edgePort;
+ }
+
+ @Override
+ protected void destroy() {
+ edgeDevice = null;
+ edgePort = null;
+ edgeLink = null;
+ }
+
+ /**
+ * Attaches the given edge link to this UI link. This method will
+ * throw an exception if this UI link is not representative of the
+ * supplied link.
+ *
+ * @param elink edge link to attach
+ * @throws IllegalArgumentException if the link is not appropriate
+ */
+ public void attachEdgeLink(EdgeLink elink) {
+ UiLinkId.Direction d = id.directionOf(elink);
+ // Expected direction of edge links is A-to-B (Host to device)
+ // but checking not null is a sufficient test
+ if (d == null) {
+ throw new IllegalArgumentException(E_UNASSOC + elink);
+ }
+
+ edgeLink = elink;
+ edgeDevice = elink.hostLocation().deviceId();
+ edgePort = elink.hostLocation().port();
+ }
+
+}
diff --git a/core/api/src/main/java/org/onosproject/ui/model/topo/UiLink.java b/core/api/src/main/java/org/onosproject/ui/model/topo/UiLink.java
index 916944c..0f65e9f 100644
--- a/core/api/src/main/java/org/onosproject/ui/model/topo/UiLink.java
+++ b/core/api/src/main/java/org/onosproject/ui/model/topo/UiLink.java
@@ -16,17 +16,11 @@
package org.onosproject.ui.model.topo;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.EdgeLink;
-import org.onosproject.net.Link;
-
-import java.util.Set;
-
import static com.google.common.base.MoreObjects.toStringHelper;
/**
- * Represents a link (line between two elements). This may have one of
- * several forms:
+ * Represents a link (line between two elements). This may be one of
+ * several concrete subclasses:
* <ul>
* <li>
* An infrastructure link:
@@ -38,17 +32,15 @@
* </li>
* <li>
* An aggregation link:
- * representing multiple underlying UI link instances.
+ * representing multiple underlying UI link instances, for example
+ * the link between two sub-regions in a region (layout).
* </li>
* </ul>
*/
-public class UiLink extends UiElement {
+public abstract class UiLink extends UiElement {
- private static final String E_UNASSOC =
- "backing link not associated with this UI link: ";
-
- private final UiTopology topology;
- private final UiLinkId id;
+ protected final UiTopology topology;
+ protected final UiLinkId id;
/**
* Creates a UI link.
@@ -61,22 +53,6 @@
this.id = id;
}
- // devices at either end of this link
- private DeviceId deviceA;
- private DeviceId deviceB;
-
- // two unidirectional links underlying this link...
- private Link linkAtoB;
- private Link linkBtoA;
-
- // ==OR== : private (synthetic) host link
- private DeviceId edgeDevice;
- private EdgeLink edgeLink;
-
- // ==OR== : set of underlying UI links that this link aggregates
- private Set<UiLink> children;
-
-
@Override
public String toString() {
return toStringHelper(this)
@@ -84,19 +60,6 @@
.toString();
}
- @Override
- protected void destroy() {
- deviceA = null;
- deviceB = null;
- linkAtoB = null;
- linkBtoA = null;
- edgeLink = null;
- if (children != null) {
- children.clear();
- children = null;
- }
- }
-
/**
* Returns the canonicalized link identifier for this link.
*
@@ -112,107 +75,25 @@
}
/**
- * Attaches the given backing link to this UI link. This method will
- * throw an exception if this UI link is not representative of the
- * supplied link.
+ * Returns the implementing class name as the type of link.
*
- * @param link backing link to attach
- * @throws IllegalArgumentException if the link is not appropriate
+ * @return link type
*/
- public void attachBackingLink(Link link) {
- UiLinkId.Direction d = id.directionOf(link);
-
- if (d == UiLinkId.Direction.A_TO_B) {
- linkAtoB = link;
- deviceA = link.src().deviceId();
- deviceB = link.dst().deviceId();
-
- } else if (d == UiLinkId.Direction.B_TO_A) {
- linkBtoA = link;
- deviceB = link.src().deviceId();
- deviceA = link.dst().deviceId();
-
- } else {
- throw new IllegalArgumentException(E_UNASSOC + link);
- }
+ public String type() {
+ return getClass().getSimpleName();
}
/**
- * Detaches the given backing link from this UI link, returning true if the
- * reverse link is still attached, or false otherwise.
+ * Returns the identifier of end-point A in string form.
*
- * @param link the backing link to detach
- * @return true if other link still attached, false otherwise
- * @throws IllegalArgumentException if the link is not appropriate
+ * @return end point A identifier
*/
- public boolean detachBackingLink(Link link) {
- UiLinkId.Direction d = id.directionOf(link);
- if (d == UiLinkId.Direction.A_TO_B) {
- linkAtoB = null;
- return linkBtoA != null;
- }
- if (d == UiLinkId.Direction.B_TO_A) {
- linkBtoA = null;
- return linkAtoB != null;
- }
- throw new IllegalArgumentException(E_UNASSOC + link);
- }
+ public abstract String endPointA();
/**
- * Attaches the given edge link to this UI link. This method will
- * throw an exception if this UI link is not representative of the
- * supplied link.
+ * Returns the identifier of end-point B in string form.
*
- * @param elink edge link to attach
- * @throws IllegalArgumentException if the link is not appropriate
+ * @return end point B identifier
*/
- public void attachEdgeLink(EdgeLink elink) {
- UiLinkId.Direction d = id.directionOf(elink);
- // Expected direction of edge links is A-to-B (Host to device)
- // but checking not null is sufficient
- if (d == null) {
- throw new IllegalArgumentException(E_UNASSOC + elink);
- }
-
- edgeLink = elink;
- edgeDevice = elink.hostLocation().deviceId();
- }
-
-
- /**
- * Returns the identity of device A.
- *
- * @return device A ID
- */
- public DeviceId deviceA() {
- return deviceA;
- }
-
- /**
- * Returns the identity of device B.
- *
- * @return device B ID
- */
- public DeviceId deviceB() {
- return deviceB;
- }
-
- /**
- * Returns backing link from A to B.
- *
- * @return backing link A to B
- */
- public Link linkAtoB() {
- return linkAtoB;
- }
-
- /**
- * Returns backing link from B to A.
- *
- * @return backing link B to A
- */
- public Link linkBtoA() {
- return linkBtoA;
- }
-
+ public abstract String endPointB();
}
diff --git a/core/api/src/main/java/org/onosproject/ui/model/topo/UiLinkId.java b/core/api/src/main/java/org/onosproject/ui/model/topo/UiLinkId.java
index e530db4..3bde82a 100644
--- a/core/api/src/main/java/org/onosproject/ui/model/topo/UiLinkId.java
+++ b/core/api/src/main/java/org/onosproject/ui/model/topo/UiLinkId.java
@@ -17,15 +17,30 @@
package org.onosproject.ui.model.topo;
import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
import org.onosproject.net.ElementId;
import org.onosproject.net.Link;
import org.onosproject.net.PortNumber;
+import org.onosproject.net.region.RegionId;
+
+import java.util.Comparator;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
/**
* A canonical representation of an identifier for {@link UiLink}s.
*/
public final class UiLinkId {
+ private static final String E_PORT_NULL = "Port number cannot be null";
+ private static final String E_DEVICE_ID_NULL = "Device ID cannot be null";
+ private static final String E_REGION_ID_NULL = "Region ID cannot be null";
+ private static final String E_IDENTICAL = "Region IDs cannot be same";
+
+ private static final Comparator<RegionId> REGION_ID_COMPARATOR =
+ (o1, o2) -> o1.toString().compareTo(o2.toString());
+
/**
* Designates the directionality of an underlying (uni-directional) link.
*/
@@ -34,12 +49,15 @@
B_TO_A
}
- private static final String CP_DELIMITER = "~";
- private static final String ID_PORT_DELIMITER = "/";
+ static final String CP_DELIMITER = "~";
+ static final String ID_PORT_DELIMITER = "/";
- private final ElementId idA;
+ private final RegionId regionA;
+ private final ElementId elementA;
private final PortNumber portA;
- private final ElementId idB;
+
+ private final RegionId regionB;
+ private final ElementId elementB;
private final PortNumber portB;
private final String idStr;
@@ -50,37 +68,81 @@
* which is invariant to whether A or B is source or destination of the
* underlying link.
*
- * @param a first element ID
+ * @param a first element ID
* @param pa first element port
- * @param b second element ID
+ * @param b second element ID
* @param pb second element port
*/
private UiLinkId(ElementId a, PortNumber pa, ElementId b, PortNumber pb) {
- idA = a;
+ elementA = a;
portA = pa;
- idB = b;
+ elementB = b;
portB = pb;
+ regionA = null;
+ regionB = null;
+
idStr = a + ID_PORT_DELIMITER + pa + CP_DELIMITER +
b + ID_PORT_DELIMITER + pb;
}
+ /**
+ * Creates a UI link identifier. It is expected that A comes before B when
+ * the two identifiers are naturally sorted.
+ *
+ * @param a first region ID
+ * @param b second region ID
+ */
+ private UiLinkId(RegionId a, RegionId b) {
+ regionA = a;
+ regionB = b;
+
+ elementA = null;
+ elementB = null;
+ portA = null;
+ portB = null;
+
+ idStr = a + CP_DELIMITER + b;
+ }
+
+ /**
+ * Creates a UI link identifier, with region at one end and a device/port
+ * at the other.
+ *
+ * @param r region ID
+ * @param d device ID
+ * @param p port number
+ */
+ private UiLinkId(RegionId r, DeviceId d, PortNumber p) {
+ regionA = r;
+ elementB = d;
+ portB = p;
+
+ regionB = null;
+ elementA = null;
+ portA = null;
+
+ idStr = r + CP_DELIMITER + elementB + ID_PORT_DELIMITER + portB;
+ }
+
@Override
public String toString() {
return idStr;
}
/**
- * Returns the identifier of the first element.
+ * Returns the identifier of the first element. Note that the returned
+ * value will be null if this identifier is for a region-region link.
*
* @return first element identity
*/
public ElementId elementA() {
- return idA;
+ return elementA;
}
/**
- * Returns the port of the first element.
+ * Returns the port of the first element. Note that the returned
+ * value will be null if this identifier is for a region-region link.
*
* @return first element port
*/
@@ -89,16 +151,18 @@
}
/**
- * Returns the identifier of the second element.
+ * Returns the identifier of the second element. Note that the returned
+ * value will be null if this identifier is for a region-region link.
*
* @return second element identity
*/
public ElementId elementB() {
- return idB;
+ return elementB;
}
/**
- * Returns the port of the second element.
+ * Returns the port of the second element. Note that the returned
+ * value will be null if this identifier is for a region-region link.
*
* @return second element port
*/
@@ -106,6 +170,28 @@
return portB;
}
+ /**
+ * Returns the identity of the first region. Note that the returned value
+ * will be null if this identifier is for a device-device or device-host
+ * link.
+ *
+ * @return first region ID
+ */
+ public RegionId regionA() {
+ return regionA;
+ }
+
+ /**
+ * Returns the identity of the second region. Note that the returned value
+ * will be null if this identifier is for a device-device or device-host
+ * link.
+ *
+ * @return second region ID
+ */
+ public RegionId regionB() {
+ return regionB;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) {
@@ -134,8 +220,8 @@
Direction directionOf(Link link) {
ConnectPoint src = link.src();
ElementId srcId = src.elementId();
- return idA.equals(srcId) ? Direction.A_TO_B
- : idB.equals(srcId) ? Direction.B_TO_A
+ return elementA.equals(srcId) ? Direction.A_TO_B
+ : elementB.equals(srcId) ? Direction.B_TO_A
: null;
}
@@ -161,4 +247,42 @@
return comp <= 0 ? new UiLinkId(srcId, src.port(), dstId, dst.port())
: new UiLinkId(dstId, dst.port(), srcId, src.port());
}
+
+ /**
+ * Generates the canonical link identifier for a link between the
+ * specified region nodes.
+ *
+ * @param one the first region ID
+ * @param two the second region ID
+ * @return link identifier
+ * @throws NullPointerException if any of the required fields are null
+ * @throws IllegalArgumentException if the identifiers are identical
+ */
+ public static UiLinkId uiLinkId(RegionId one, RegionId two) {
+ checkNotNull(one, E_REGION_ID_NULL);
+ checkNotNull(two, E_REGION_ID_NULL);
+ checkArgument(!one.equals(two), E_IDENTICAL);
+
+ boolean flip = REGION_ID_COMPARATOR.compare(one, two) > 0;
+ return flip ? new UiLinkId(two, one) : new UiLinkId(one, two);
+ }
+
+ /**
+ * Generates the canonical link identifier for a link between the specified
+ * region and device/port.
+ *
+ * @param regionId region ID
+ * @param deviceId device ID
+ * @param portNumber port number
+ * @return link identifier
+ * @throws NullPointerException if any of the required fields are null
+ */
+ public static UiLinkId uiLinkId(RegionId regionId, DeviceId deviceId,
+ PortNumber portNumber) {
+ checkNotNull(regionId, E_REGION_ID_NULL);
+ checkNotNull(deviceId, E_DEVICE_ID_NULL);
+ checkNotNull(portNumber, E_PORT_NULL);
+
+ return new UiLinkId(regionId, deviceId, portNumber);
+ }
}
diff --git a/core/api/src/main/java/org/onosproject/ui/model/topo/UiRegion.java b/core/api/src/main/java/org/onosproject/ui/model/topo/UiRegion.java
index b3185f1..87d5036 100644
--- a/core/api/src/main/java/org/onosproject/ui/model/topo/UiRegion.java
+++ b/core/api/src/main/java/org/onosproject/ui/model/topo/UiRegion.java
@@ -53,7 +53,6 @@
// loose bindings to things in this region
private final Set<DeviceId> deviceIds = new HashSet<>();
private final Set<HostId> hostIds = new HashSet<>();
- private final Set<UiLinkId> uiLinkIds = new HashSet<>();
private final List<String> layerOrder = new ArrayList<>();
@@ -84,7 +83,6 @@
protected void destroy() {
deviceIds.clear();
hostIds.clear();
- uiLinkIds.clear();
}
/**
@@ -135,6 +133,15 @@
}
/**
+ * Returns the UI region that is the parent of this region.
+ *
+ * @return the parent region
+ */
+ public UiRegion parentRegion() {
+ return topology.findRegion(parent);
+ }
+
+ /**
* Sets the parent ID for this region.
*
* @param parentId parent ID
@@ -192,7 +199,6 @@
.add("kids", kids)
.add("devices", deviceIds)
.add("#hosts", hostIds.size())
- .add("#links", uiLinkIds.size())
.toString();
}
@@ -252,24 +258,6 @@
}
/**
- * Returns the set of link identifiers for this region.
- *
- * @return link identifiers for this region
- */
- public Set<UiLinkId> linkIds() {
- return ImmutableSet.copyOf(uiLinkIds);
- }
-
- /**
- * Returns the links in this region.
- *
- * @return the links in this region
- */
- public Set<UiLink> links() {
- return topology.linkSet(uiLinkIds);
- }
-
- /**
* Returns the order in which layers should be rendered. Lower layers
* come earlier in the list. For example, to indicate that nodes in the
* optical layer should be rendered "below" nodes in the packet layer,
diff --git a/core/api/src/main/java/org/onosproject/ui/model/topo/UiRegionDeviceLink.java b/core/api/src/main/java/org/onosproject/ui/model/topo/UiRegionDeviceLink.java
new file mode 100644
index 0000000..f749473
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/ui/model/topo/UiRegionDeviceLink.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2016-present 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.onosproject.ui.model.topo;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.region.RegionId;
+
+
+/**
+ * Designates a link between a region node and a device.
+ */
+public class UiRegionDeviceLink extends UiLink {
+
+ private static final String E_NOT_REGION_DEVICE_ID =
+ "UI link identifier not region to device";
+
+ // private (synthetic) region-device link
+ private final RegionId region;
+ private final DeviceId device;
+ private final PortNumber port;
+
+ /**
+ * Creates a region to device UI link. Note that it is expected that the
+ * link identifier is one that has a region ID at one end, and a device
+ * ID at the other
+ *
+ * @param topology parent topology
+ * @param id canonicalized link identifier
+ * @throws IllegalArgumentException if the link ID is not region-region
+ */
+ public UiRegionDeviceLink(UiTopology topology, UiLinkId id) {
+ super(topology, id);
+ region = id.regionA();
+ device = (DeviceId) id.elementB();
+ port = id.portB();
+ if (region == null || device == null || port == null) {
+ throw new IllegalArgumentException(E_NOT_REGION_DEVICE_ID);
+ }
+ }
+
+ @Override
+ public String endPointA() {
+ return region.id();
+ }
+
+ @Override
+ public String endPointB() {
+ return device + UiLinkId.ID_PORT_DELIMITER + port;
+ }
+
+ /**
+ * Returns the identity of the region.
+ *
+ * @return region ID
+ */
+ public RegionId region() {
+ return region;
+ }
+
+ /**
+ * Returns the identity of the device.
+ *
+ * @return device ID
+ */
+ public DeviceId device() {
+ return device;
+ }
+
+ /**
+ * Returns the identity of the device port.
+ *
+ * @return device port number
+ */
+ public PortNumber port() {
+ return port;
+ }
+}
diff --git a/core/api/src/main/java/org/onosproject/ui/model/topo/UiRegionLink.java b/core/api/src/main/java/org/onosproject/ui/model/topo/UiRegionLink.java
new file mode 100644
index 0000000..7c3456c
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/ui/model/topo/UiRegionLink.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2016-present 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.onosproject.ui.model.topo;
+
+import org.onosproject.net.region.RegionId;
+
+
+/**
+ * Designates a link between two region nodes.
+ */
+public class UiRegionLink extends UiLink {
+
+ private static final String E_NOT_REGION_ID =
+ "UI link identifier not region to region";
+
+ // private (synthetic) region - region link
+ private final RegionId regionA;
+ private final RegionId regionB;
+
+ /**
+ * Creates a region to region UI link. Note that it is expected that the
+ * link identifier is one that has region IDs as source and destination.
+ *
+ * @param topology parent topology
+ * @param id canonicalized link identifier
+ * @throws IllegalArgumentException if the link ID is not region-region
+ */
+ public UiRegionLink(UiTopology topology, UiLinkId id) {
+ super(topology, id);
+ regionA = id.regionA();
+ regionB = id.regionB();
+ if (regionA == null || regionB == null) {
+ throw new IllegalArgumentException(E_NOT_REGION_ID);
+ }
+ }
+
+ @Override
+ public String endPointA() {
+ return regionA.id();
+ }
+
+ @Override
+ public String endPointB() {
+ return regionB.id();
+ }
+
+ /**
+ * Returns the identity of the first region.
+ *
+ * @return first region ID
+ */
+ public RegionId regionA() {
+ return regionA;
+ }
+
+ /**
+ * Returns the identity of the second region.
+ *
+ * @return second region ID
+ */
+ public RegionId regionB() {
+ return regionB;
+ }
+}
diff --git a/core/api/src/main/java/org/onosproject/ui/model/topo/UiSynthLink.java b/core/api/src/main/java/org/onosproject/ui/model/topo/UiSynthLink.java
new file mode 100644
index 0000000..7e67430
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/ui/model/topo/UiSynthLink.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2016-present 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.onosproject.ui.model.topo;
+
+import org.onosproject.net.region.RegionId;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * A synthetic link that encapsulates a UiLink instance and the region to
+ * which it belongs.
+ */
+public class UiSynthLink {
+
+ private final RegionId regionId;
+ private final UiLink link;
+
+ /**
+ * Constructs a synthetic link with the given parameters.
+ *
+ * @param regionId the region to which the link belongs
+ * @param link the link instance
+ */
+ public UiSynthLink(RegionId regionId, UiLink link) {
+ this.regionId = regionId;
+ this.link = link;
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this)
+ .add("region", regionId)
+ .add("link", link)
+ .toString();
+ }
+
+ /**
+ * Returns the region identifier.
+ *
+ * @return the region ID
+ */
+ public RegionId regionId() {
+ return regionId;
+ }
+
+ /**
+ * Returns the link.
+ *
+ * @return the link
+ */
+ public UiLink link() {
+ return link;
+ }
+}
diff --git a/core/api/src/main/java/org/onosproject/ui/model/topo/UiTopology.java b/core/api/src/main/java/org/onosproject/ui/model/topo/UiTopology.java
index 708fd18..3297608 100644
--- a/core/api/src/main/java/org/onosproject/ui/model/topo/UiTopology.java
+++ b/core/api/src/main/java/org/onosproject/ui/model/topo/UiTopology.java
@@ -19,6 +19,7 @@
import org.onosproject.cluster.NodeId;
import org.onosproject.net.DeviceId;
import org.onosproject.net.HostId;
+import org.onosproject.net.PortNumber;
import org.onosproject.net.region.RegionId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -30,9 +31,12 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
+import java.util.stream.Collectors;
import static com.google.common.base.MoreObjects.toStringHelper;
+import static org.onosproject.ui.model.topo.UiLinkId.uiLinkId;
/**
* Represents the overall network topology.
@@ -59,7 +63,11 @@
private final Map<RegionId, UiRegion> regionLookup = new HashMap<>();
private final Map<DeviceId, UiDevice> deviceLookup = new HashMap<>();
private final Map<HostId, UiHost> hostLookup = new HashMap<>();
- private final Map<UiLinkId, UiLink> linkLookup = new HashMap<>();
+ private final Map<UiLinkId, UiDeviceLink> devLinkLookup = new HashMap<>();
+ private final Map<UiLinkId, UiEdgeLink> edgeLinkLookup = new HashMap<>();
+
+ // a cache of the computed synthetic links
+ private final List<UiSynthLink> synthLinks = new ArrayList<>();
// a container for devices, hosts, etc. belonging to no region
private final UiRegion nullRegion = new UiRegion(this, null);
@@ -72,7 +80,9 @@
.add("#regions", regionCount())
.add("#devices", deviceLookup.size())
.add("#hosts", hostLookup.size())
- .add("#links", linkLookup.size())
+ .add("#dev-links", devLinkLookup.size())
+ .add("#edge-links", edgeLinkLookup.size())
+ .add("#synth-links", synthLinks.size())
.toString();
}
@@ -91,7 +101,10 @@
regionLookup.clear();
deviceLookup.clear();
hostLookup.clear();
- linkLookup.clear();
+ devLinkLookup.clear();
+ edgeLinkLookup.clear();
+
+ synthLinks.clear();
nullRegion.destroy();
}
@@ -261,54 +274,96 @@
return deviceLookup.size();
}
+
/**
- * Returns all links in the model.
+ * Returns all device links in the model.
*
- * @return all links
+ * @return all device links
*/
- public Set<UiLink> allLinks() {
- return new HashSet<>(linkLookup.values());
+ public Set<UiDeviceLink> allDeviceLinks() {
+ return new HashSet<>(devLinkLookup.values());
}
/**
- * Returns the link with the specified identifier, or null if no such
- * link exists.
+ * Returns the device link with the specified identifier, or null if no
+ * such link exists.
*
* @param id the canonicalized link identifier
- * @return corresponding UI link
+ * @return corresponding UI device link
*/
- public UiLink findLink(UiLinkId id) {
- return linkLookup.get(id);
+ public UiDeviceLink findDeviceLink(UiLinkId id) {
+ return devLinkLookup.get(id);
}
/**
- * Adds the given UI link to the topology model.
+ * Returns the edge link with the specified identifier, or null if no
+ * such link exists.
*
- * @param uiLink link to add
+ * @param id the canonicalized link identifier
+ * @return corresponding UI edge link
*/
- public void add(UiLink uiLink) {
- linkLookup.put(uiLink.id(), uiLink);
+ public UiEdgeLink findEdgeLink(UiLinkId id) {
+ return edgeLinkLookup.get(id);
}
/**
- * Removes the given UI link from the model.
+ * Adds the given UI device link to the topology model.
*
- * @param uiLink link to remove
+ * @param uiDeviceLink link to add
*/
- public void remove(UiLink uiLink) {
- UiLink link = linkLookup.remove(uiLink.id());
+ public void add(UiDeviceLink uiDeviceLink) {
+ devLinkLookup.put(uiDeviceLink.id(), uiDeviceLink);
+ }
+
+ /**
+ * Adds the given UI edge link to the topology model.
+ *
+ * @param uiEdgeLink link to add
+ */
+ public void add(UiEdgeLink uiEdgeLink) {
+ edgeLinkLookup.put(uiEdgeLink.id(), uiEdgeLink);
+ }
+
+ /**
+ * Removes the given UI device link from the model.
+ *
+ * @param uiDeviceLink link to remove
+ */
+ public void remove(UiDeviceLink uiDeviceLink) {
+ UiDeviceLink link = devLinkLookup.remove(uiDeviceLink.id());
if (link != null) {
link.destroy();
}
}
/**
- * Returns the number of links configured in the topology.
+ * Removes the given UI edge link from the model.
*
- * @return number of links
+ * @param uiEdgeLink link to remove
*/
- public int linkCount() {
- return linkLookup.size();
+ public void remove(UiEdgeLink uiEdgeLink) {
+ UiEdgeLink link = edgeLinkLookup.remove(uiEdgeLink.id());
+ if (link != null) {
+ link.destroy();
+ }
+ }
+
+ /**
+ * Returns the number of device links configured in the topology.
+ *
+ * @return number of device links
+ */
+ public int deviceLinkCount() {
+ return devLinkLookup.size();
+ }
+
+ /**
+ * Returns the number of edge links configured in the topology.
+ *
+ * @return number of edge links
+ */
+ public int edgeLinkCount() {
+ return edgeLinkLookup.size();
}
/**
@@ -405,22 +460,198 @@
}
/**
- * Returns the set of UI links with the given identifiers.
+ * Returns the set of UI device links with the given identifiers.
*
* @param uiLinkIds link identifiers
- * @return set of matching UI link instances
+ * @return set of matching UI device link instances
*/
- Set<UiLink> linkSet(Set<UiLinkId> uiLinkIds) {
- Set<UiLink> uiLinks = new HashSet<>();
+ Set<UiDeviceLink> linkSet(Set<UiLinkId> uiLinkIds) {
+ Set<UiDeviceLink> result = new HashSet<>();
for (UiLinkId id : uiLinkIds) {
- UiLink link = linkLookup.get(id);
+ UiDeviceLink link = devLinkLookup.get(id);
if (link != null) {
- uiLinks.add(link);
+ result.add(link);
} else {
- log.warn(E_UNMAPPED, "link", id);
+ log.warn(E_UNMAPPED, "device link", id);
}
}
- return uiLinks;
+ return result;
+ }
+
+ /**
+ * Uses the device-device links and data about the regions to compute the
+ * set of synthetic links that are required per region.
+ */
+ public void computeSynthLinks() {
+ List<UiSynthLink> slinks = new ArrayList<>();
+ allDeviceLinks().forEach((link) -> {
+ UiSynthLink synthetic = inferSyntheticLink(link);
+ slinks.add(synthetic);
+ log.debug("Synthetic link: {}", synthetic);
+ });
+
+ synthLinks.clear();
+ synthLinks.addAll(slinks);
+ }
+
+ private UiSynthLink inferSyntheticLink(UiDeviceLink link) {
+ /*
+ Look at the containment hierarchy of each end of the link. Find the
+ common ancestor region R. A synthetic link will be added to R, based
+ on the "next" node back down the branch...
+
+ S1 --- S2 * in the same region ...
+ : :
+ R R return S1 --- S2 (same link instance)
+
+
+ S1 --- S2 * in different regions (R1, R2) at same level
+ : :
+ R1 R2 return R1 --- R2
+ : :
+ R R
+
+ S1 --- S2 * in different regions at different levels
+ : :
+ R1 R2 return R1 --- R3
+ : :
+ R R3
+ :
+ R
+
+ S1 --- S2 * in different regions at different levels
+ : :
+ R R2 return S1 --- R2
+ :
+ R
+
+ */
+ DeviceId a = link.deviceA();
+ DeviceId b = link.deviceB();
+ List<RegionId> aBranch = ancestors(a);
+ List<RegionId> bBranch = ancestors(b);
+ if (aBranch == null || bBranch == null) {
+ return null;
+ }
+
+ return makeSynthLink(link, aBranch, bBranch);
+ }
+
+ // package private for unit testing
+ UiSynthLink makeSynthLink(UiDeviceLink orig,
+ List<RegionId> aBranch,
+ List<RegionId> bBranch) {
+
+ final int aSize = aBranch.size();
+ final int bSize = bBranch.size();
+ final int min = Math.min(aSize, bSize);
+
+ int index = 0;
+ RegionId commonRegion = aBranch.get(index);
+
+ while (true) {
+ int next = index + 1;
+ if (next == min) {
+ // no more pairs of regions left to test
+ break;
+ }
+ RegionId rA = aBranch.get(next);
+ RegionId rB = bBranch.get(next);
+ if (rA.equals(rB)) {
+ commonRegion = rA;
+ index++;
+ } else {
+ break;
+ }
+ }
+
+
+ int endPointIndex = index + 1;
+ UiLinkId linkId;
+ UiLink link;
+
+ if (endPointIndex < aSize) {
+ // the A endpoint is a subregion
+ RegionId aRegion = aBranch.get(endPointIndex);
+
+ if (endPointIndex < bSize) {
+ // the B endpoint is a subregion
+ RegionId bRegion = bBranch.get(endPointIndex);
+
+ linkId = uiLinkId(aRegion, bRegion);
+ link = new UiRegionLink(this, linkId);
+
+ } else {
+ // the B endpoint is the device
+ DeviceId dB = orig.deviceB();
+ PortNumber pB = orig.portB();
+
+ linkId = uiLinkId(aRegion, dB, pB);
+ link = new UiRegionDeviceLink(this, linkId);
+ }
+
+ } else {
+ // the A endpoint is the device
+ DeviceId dA = orig.deviceA();
+ PortNumber pA = orig.portA();
+
+ if (endPointIndex < bSize) {
+ // the B endpoint is a subregion
+ RegionId bRegion = bBranch.get(endPointIndex);
+
+ linkId = uiLinkId(bRegion, dA, pA);
+ link = new UiRegionDeviceLink(this, linkId);
+
+ } else {
+ // the B endpoint is the device
+ // (so, we can just use the original device-device link...)
+
+ link = orig;
+ }
+ }
+ return new UiSynthLink(commonRegion, link);
+ }
+
+ private List<RegionId> ancestors(DeviceId id) {
+ // return the ancestor chain from this device to root region
+ UiDevice dev = findDevice(id);
+ if (dev == null) {
+ log.warn("Unable to find cached device with ID %s", id);
+ return null;
+ }
+
+ UiRegion r = dev.uiRegion();
+ List<RegionId> result = new ArrayList<>();
+ while (r != null && !r.isRoot()) {
+ result.add(0, r.id());
+ r = r.parentRegion();
+ }
+ // finally add root region, since this is the grand-daddy of them all
+ result.add(0, UiRegion.NULL_ID);
+ return result;
+ }
+
+
+ /**
+ * Returns the synthetic links associated with the specified region.
+ *
+ * @param regionId the region ID
+ * @return synthetic links for this region
+ */
+ public List<UiSynthLink> findSynthLinks(RegionId regionId) {
+ return synthLinks.stream()
+ .filter(s -> Objects.equals(regionId, s.regionId()))
+ .collect(Collectors.toList());
+ }
+
+
+ /**
+ * Returns the number of synthetic links in the topology.
+ *
+ * @return the synthetic link count
+ */
+ public int synthLinkCount() {
+ return synthLinks.size();
}
/**
@@ -452,13 +683,22 @@
sb.append(INDENT_2).append(h).append(EOL);
}
- sb.append(INDENT_1).append("Links").append(EOL);
- for (UiLink link : linkLookup.values()) {
+ sb.append(INDENT_1).append("Device Links").append(EOL);
+ for (UiLink link : devLinkLookup.values()) {
+ sb.append(INDENT_2).append(link).append(EOL);
+ }
+
+ sb.append(INDENT_1).append("Edge Links").append(EOL);
+ for (UiLink link : edgeLinkLookup.values()) {
+ sb.append(INDENT_2).append(link).append(EOL);
+ }
+
+ sb.append(INDENT_1).append("Synth Links").append(EOL);
+ for (UiSynthLink link : synthLinks) {
sb.append(INDENT_2).append(link).append(EOL);
}
sb.append("------").append(EOL);
return sb.toString();
}
-
}