Simon Hunt
Committed by Gerrit Code Review

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
Showing 21 changed files with 1499 additions and 305 deletions
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.ui.model.topo;
18 +
19 +import org.onosproject.net.DeviceId;
20 +import org.onosproject.net.Link;
21 +import org.onosproject.net.PortNumber;
22 +
23 +/**
24 + * Represents a link between two devices; that is, an infrastructure link.
25 + */
26 +public class UiDeviceLink extends UiLink {
27 +
28 + private static final String E_UNASSOC =
29 + "backing link not associated with this UI device link: ";
30 +
31 + // devices and ports at either end of this link
32 + private DeviceId deviceA;
33 + private DeviceId deviceB;
34 + private PortNumber portA;
35 + private PortNumber portB;
36 +
37 + // two unidirectional links underlying this link...
38 + private Link linkAtoB;
39 + private Link linkBtoA;
40 +
41 +
42 + /**
43 + * Creates a device to device UI link.
44 + *
45 + * @param topology parent topology
46 + * @param id canonicalized link identifier
47 + */
48 + public UiDeviceLink(UiTopology topology, UiLinkId id) {
49 + super(topology, id);
50 + }
51 +
52 + @Override
53 + public String endPointA() {
54 + return deviceA + UiLinkId.ID_PORT_DELIMITER + portA;
55 + }
56 +
57 + @Override
58 + public String endPointB() {
59 + return deviceB + UiLinkId.ID_PORT_DELIMITER + portB;
60 + }
61 +
62 +
63 + @Override
64 + protected void destroy() {
65 + deviceA = null;
66 + deviceB = null;
67 + portA = null;
68 + portB = null;
69 + linkAtoB = null;
70 + linkBtoA = null;
71 + }
72 +
73 +
74 + /**
75 + * Attaches the given backing link to this UI link. This method will
76 + * throw an exception if this UI link is not representative of the
77 + * supplied link.
78 + *
79 + * @param link backing link to attach
80 + * @throws IllegalArgumentException if the link is not appropriate
81 + */
82 + public void attachBackingLink(Link link) {
83 + UiLinkId.Direction d = id.directionOf(link);
84 +
85 + if (d == UiLinkId.Direction.A_TO_B) {
86 + linkAtoB = link;
87 + deviceA = link.src().deviceId();
88 + portA = link.src().port();
89 + deviceB = link.dst().deviceId();
90 + portB = link.dst().port();
91 +
92 + } else if (d == UiLinkId.Direction.B_TO_A) {
93 + linkBtoA = link;
94 + deviceB = link.src().deviceId();
95 + portB = link.src().port();
96 + deviceA = link.dst().deviceId();
97 + portA = link.dst().port();
98 +
99 + } else {
100 + throw new IllegalArgumentException(E_UNASSOC + link);
101 + }
102 + }
103 +
104 + /**
105 + * Detaches the given backing link from this UI link, returning true if the
106 + * reverse link is still attached, or false otherwise.
107 + *
108 + * @param link the backing link to detach
109 + * @return true if other link still attached, false otherwise
110 + * @throws IllegalArgumentException if the link is not appropriate
111 + */
112 + public boolean detachBackingLink(Link link) {
113 + UiLinkId.Direction d = id.directionOf(link);
114 + if (d == UiLinkId.Direction.A_TO_B) {
115 + linkAtoB = null;
116 + return linkBtoA != null;
117 + }
118 + if (d == UiLinkId.Direction.B_TO_A) {
119 + linkBtoA = null;
120 + return linkAtoB != null;
121 + }
122 + throw new IllegalArgumentException(E_UNASSOC + link);
123 + }
124 +
125 +
126 + /**
127 + * Returns the identity of device A.
128 + *
129 + * @return device A ID
130 + */
131 + public DeviceId deviceA() {
132 + return deviceA;
133 + }
134 +
135 + /**
136 + * Returns the port number of device A.
137 + *
138 + * @return port A
139 + */
140 + public PortNumber portA() {
141 + return portA;
142 + }
143 +
144 + /**
145 + * Returns the identity of device B.
146 + *
147 + * @return device B ID
148 + */
149 + public DeviceId deviceB() {
150 + return deviceB;
151 + }
152 +
153 + /**
154 + * Returns the port number of device B.
155 + *
156 + * @return port B
157 + */
158 + public PortNumber portB() {
159 + return portB;
160 + }
161 +
162 + /**
163 + * Returns backing link from A to B.
164 + *
165 + * @return backing link A to B
166 + */
167 + public Link linkAtoB() {
168 + return linkAtoB;
169 + }
170 +
171 + /**
172 + * Returns backing link from B to A.
173 + *
174 + * @return backing link B to A
175 + */
176 + public Link linkBtoA() {
177 + return linkBtoA;
178 + }
179 +
180 +}
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.ui.model.topo;
18 +
19 +import org.onosproject.net.DeviceId;
20 +import org.onosproject.net.EdgeLink;
21 +import org.onosproject.net.PortNumber;
22 +
23 +/**
24 + * Designates a link between a device and a host; that is, an edge link.
25 + */
26 +public class UiEdgeLink extends UiLink {
27 +
28 + private static final String E_UNASSOC =
29 + "backing link not associated with this UI edge link: ";
30 +
31 + // private (synthetic) host link
32 + private DeviceId edgeDevice;
33 + private PortNumber edgePort;
34 + private EdgeLink edgeLink;
35 +
36 + /**
37 + * Creates a UI link.
38 + *
39 + * @param topology parent topology
40 + * @param id canonicalized link identifier
41 + */
42 + public UiEdgeLink(UiTopology topology, UiLinkId id) {
43 + super(topology, id);
44 + }
45 +
46 + @Override
47 + public String endPointA() {
48 + return edgeLink.hostId().toString();
49 + }
50 +
51 + @Override
52 + public String endPointB() {
53 + return edgeDevice + UiLinkId.ID_PORT_DELIMITER + edgePort;
54 + }
55 +
56 + @Override
57 + protected void destroy() {
58 + edgeDevice = null;
59 + edgePort = null;
60 + edgeLink = null;
61 + }
62 +
63 + /**
64 + * Attaches the given edge link to this UI link. This method will
65 + * throw an exception if this UI link is not representative of the
66 + * supplied link.
67 + *
68 + * @param elink edge link to attach
69 + * @throws IllegalArgumentException if the link is not appropriate
70 + */
71 + public void attachEdgeLink(EdgeLink elink) {
72 + UiLinkId.Direction d = id.directionOf(elink);
73 + // Expected direction of edge links is A-to-B (Host to device)
74 + // but checking not null is a sufficient test
75 + if (d == null) {
76 + throw new IllegalArgumentException(E_UNASSOC + elink);
77 + }
78 +
79 + edgeLink = elink;
80 + edgeDevice = elink.hostLocation().deviceId();
81 + edgePort = elink.hostLocation().port();
82 + }
83 +
84 +}
...@@ -16,17 +16,11 @@ ...@@ -16,17 +16,11 @@
16 16
17 package org.onosproject.ui.model.topo; 17 package org.onosproject.ui.model.topo;
18 18
19 -import org.onosproject.net.DeviceId;
20 -import org.onosproject.net.EdgeLink;
21 -import org.onosproject.net.Link;
22 -
23 -import java.util.Set;
24 -
25 import static com.google.common.base.MoreObjects.toStringHelper; 19 import static com.google.common.base.MoreObjects.toStringHelper;
26 20
27 /** 21 /**
28 - * Represents a link (line between two elements). This may have one of 22 + * Represents a link (line between two elements). This may be one of
29 - * several forms: 23 + * several concrete subclasses:
30 * <ul> 24 * <ul>
31 * <li> 25 * <li>
32 * An infrastructure link: 26 * An infrastructure link:
...@@ -38,17 +32,15 @@ import static com.google.common.base.MoreObjects.toStringHelper; ...@@ -38,17 +32,15 @@ import static com.google.common.base.MoreObjects.toStringHelper;
38 * </li> 32 * </li>
39 * <li> 33 * <li>
40 * An aggregation link: 34 * An aggregation link:
41 - * representing multiple underlying UI link instances. 35 + * representing multiple underlying UI link instances, for example
36 + * the link between two sub-regions in a region (layout).
42 * </li> 37 * </li>
43 * </ul> 38 * </ul>
44 */ 39 */
45 -public class UiLink extends UiElement { 40 +public abstract class UiLink extends UiElement {
46 -
47 - private static final String E_UNASSOC =
48 - "backing link not associated with this UI link: ";
49 41
50 - private final UiTopology topology; 42 + protected final UiTopology topology;
51 - private final UiLinkId id; 43 + protected final UiLinkId id;
52 44
53 /** 45 /**
54 * Creates a UI link. 46 * Creates a UI link.
...@@ -61,22 +53,6 @@ public class UiLink extends UiElement { ...@@ -61,22 +53,6 @@ public class UiLink extends UiElement {
61 this.id = id; 53 this.id = id;
62 } 54 }
63 55
64 - // devices at either end of this link
65 - private DeviceId deviceA;
66 - private DeviceId deviceB;
67 -
68 - // two unidirectional links underlying this link...
69 - private Link linkAtoB;
70 - private Link linkBtoA;
71 -
72 - // ==OR== : private (synthetic) host link
73 - private DeviceId edgeDevice;
74 - private EdgeLink edgeLink;
75 -
76 - // ==OR== : set of underlying UI links that this link aggregates
77 - private Set<UiLink> children;
78 -
79 -
80 @Override 56 @Override
81 public String toString() { 57 public String toString() {
82 return toStringHelper(this) 58 return toStringHelper(this)
...@@ -84,19 +60,6 @@ public class UiLink extends UiElement { ...@@ -84,19 +60,6 @@ public class UiLink extends UiElement {
84 .toString(); 60 .toString();
85 } 61 }
86 62
87 - @Override
88 - protected void destroy() {
89 - deviceA = null;
90 - deviceB = null;
91 - linkAtoB = null;
92 - linkBtoA = null;
93 - edgeLink = null;
94 - if (children != null) {
95 - children.clear();
96 - children = null;
97 - }
98 - }
99 -
100 /** 63 /**
101 * Returns the canonicalized link identifier for this link. 64 * Returns the canonicalized link identifier for this link.
102 * 65 *
...@@ -112,107 +75,25 @@ public class UiLink extends UiElement { ...@@ -112,107 +75,25 @@ public class UiLink extends UiElement {
112 } 75 }
113 76
114 /** 77 /**
115 - * Attaches the given backing link to this UI link. This method will 78 + * Returns the implementing class name as the type of link.
116 - * throw an exception if this UI link is not representative of the
117 - * supplied link.
118 - *
119 - * @param link backing link to attach
120 - * @throws IllegalArgumentException if the link is not appropriate
121 - */
122 - public void attachBackingLink(Link link) {
123 - UiLinkId.Direction d = id.directionOf(link);
124 -
125 - if (d == UiLinkId.Direction.A_TO_B) {
126 - linkAtoB = link;
127 - deviceA = link.src().deviceId();
128 - deviceB = link.dst().deviceId();
129 -
130 - } else if (d == UiLinkId.Direction.B_TO_A) {
131 - linkBtoA = link;
132 - deviceB = link.src().deviceId();
133 - deviceA = link.dst().deviceId();
134 -
135 - } else {
136 - throw new IllegalArgumentException(E_UNASSOC + link);
137 - }
138 - }
139 -
140 - /**
141 - * Detaches the given backing link from this UI link, returning true if the
142 - * reverse link is still attached, or false otherwise.
143 - *
144 - * @param link the backing link to detach
145 - * @return true if other link still attached, false otherwise
146 - * @throws IllegalArgumentException if the link is not appropriate
147 - */
148 - public boolean detachBackingLink(Link link) {
149 - UiLinkId.Direction d = id.directionOf(link);
150 - if (d == UiLinkId.Direction.A_TO_B) {
151 - linkAtoB = null;
152 - return linkBtoA != null;
153 - }
154 - if (d == UiLinkId.Direction.B_TO_A) {
155 - linkBtoA = null;
156 - return linkAtoB != null;
157 - }
158 - throw new IllegalArgumentException(E_UNASSOC + link);
159 - }
160 -
161 - /**
162 - * Attaches the given edge link to this UI link. This method will
163 - * throw an exception if this UI link is not representative of the
164 - * supplied link.
165 - *
166 - * @param elink edge link to attach
167 - * @throws IllegalArgumentException if the link is not appropriate
168 - */
169 - public void attachEdgeLink(EdgeLink elink) {
170 - UiLinkId.Direction d = id.directionOf(elink);
171 - // Expected direction of edge links is A-to-B (Host to device)
172 - // but checking not null is sufficient
173 - if (d == null) {
174 - throw new IllegalArgumentException(E_UNASSOC + elink);
175 - }
176 -
177 - edgeLink = elink;
178 - edgeDevice = elink.hostLocation().deviceId();
179 - }
180 -
181 -
182 - /**
183 - * Returns the identity of device A.
184 * 79 *
185 - * @return device A ID 80 + * @return link type
186 */ 81 */
187 - public DeviceId deviceA() { 82 + public String type() {
188 - return deviceA; 83 + return getClass().getSimpleName();
189 } 84 }
190 85
191 /** 86 /**
192 - * Returns the identity of device B. 87 + * Returns the identifier of end-point A in string form.
193 * 88 *
194 - * @return device B ID 89 + * @return end point A identifier
195 */ 90 */
196 - public DeviceId deviceB() { 91 + public abstract String endPointA();
197 - return deviceB;
198 - }
199 92
200 /** 93 /**
201 - * Returns backing link from A to B. 94 + * Returns the identifier of end-point B in string form.
202 * 95 *
203 - * @return backing link A to B 96 + * @return end point B identifier
204 */ 97 */
205 - public Link linkAtoB() { 98 + public abstract String endPointB();
206 - return linkAtoB;
207 - }
208 -
209 - /**
210 - * Returns backing link from B to A.
211 - *
212 - * @return backing link B to A
213 - */
214 - public Link linkBtoA() {
215 - return linkBtoA;
216 - }
217 -
218 } 99 }
......
...@@ -17,15 +17,30 @@ ...@@ -17,15 +17,30 @@
17 package org.onosproject.ui.model.topo; 17 package org.onosproject.ui.model.topo;
18 18
19 import org.onosproject.net.ConnectPoint; 19 import org.onosproject.net.ConnectPoint;
20 +import org.onosproject.net.DeviceId;
20 import org.onosproject.net.ElementId; 21 import org.onosproject.net.ElementId;
21 import org.onosproject.net.Link; 22 import org.onosproject.net.Link;
22 import org.onosproject.net.PortNumber; 23 import org.onosproject.net.PortNumber;
24 +import org.onosproject.net.region.RegionId;
25 +
26 +import java.util.Comparator;
27 +
28 +import static com.google.common.base.Preconditions.checkArgument;
29 +import static com.google.common.base.Preconditions.checkNotNull;
23 30
24 /** 31 /**
25 * A canonical representation of an identifier for {@link UiLink}s. 32 * A canonical representation of an identifier for {@link UiLink}s.
26 */ 33 */
27 public final class UiLinkId { 34 public final class UiLinkId {
28 35
36 + private static final String E_PORT_NULL = "Port number cannot be null";
37 + private static final String E_DEVICE_ID_NULL = "Device ID cannot be null";
38 + private static final String E_REGION_ID_NULL = "Region ID cannot be null";
39 + private static final String E_IDENTICAL = "Region IDs cannot be same";
40 +
41 + private static final Comparator<RegionId> REGION_ID_COMPARATOR =
42 + (o1, o2) -> o1.toString().compareTo(o2.toString());
43 +
29 /** 44 /**
30 * Designates the directionality of an underlying (uni-directional) link. 45 * Designates the directionality of an underlying (uni-directional) link.
31 */ 46 */
...@@ -34,12 +49,15 @@ public final class UiLinkId { ...@@ -34,12 +49,15 @@ public final class UiLinkId {
34 B_TO_A 49 B_TO_A
35 } 50 }
36 51
37 - private static final String CP_DELIMITER = "~"; 52 + static final String CP_DELIMITER = "~";
38 - private static final String ID_PORT_DELIMITER = "/"; 53 + static final String ID_PORT_DELIMITER = "/";
39 54
40 - private final ElementId idA; 55 + private final RegionId regionA;
56 + private final ElementId elementA;
41 private final PortNumber portA; 57 private final PortNumber portA;
42 - private final ElementId idB; 58 +
59 + private final RegionId regionB;
60 + private final ElementId elementB;
43 private final PortNumber portB; 61 private final PortNumber portB;
44 62
45 private final String idStr; 63 private final String idStr;
...@@ -56,31 +74,75 @@ public final class UiLinkId { ...@@ -56,31 +74,75 @@ public final class UiLinkId {
56 * @param pb second element port 74 * @param pb second element port
57 */ 75 */
58 private UiLinkId(ElementId a, PortNumber pa, ElementId b, PortNumber pb) { 76 private UiLinkId(ElementId a, PortNumber pa, ElementId b, PortNumber pb) {
59 - idA = a; 77 + elementA = a;
60 portA = pa; 78 portA = pa;
61 - idB = b; 79 + elementB = b;
62 portB = pb; 80 portB = pb;
63 81
82 + regionA = null;
83 + regionB = null;
84 +
64 idStr = a + ID_PORT_DELIMITER + pa + CP_DELIMITER + 85 idStr = a + ID_PORT_DELIMITER + pa + CP_DELIMITER +
65 b + ID_PORT_DELIMITER + pb; 86 b + ID_PORT_DELIMITER + pb;
66 } 87 }
67 88
89 + /**
90 + * Creates a UI link identifier. It is expected that A comes before B when
91 + * the two identifiers are naturally sorted.
92 + *
93 + * @param a first region ID
94 + * @param b second region ID
95 + */
96 + private UiLinkId(RegionId a, RegionId b) {
97 + regionA = a;
98 + regionB = b;
99 +
100 + elementA = null;
101 + elementB = null;
102 + portA = null;
103 + portB = null;
104 +
105 + idStr = a + CP_DELIMITER + b;
106 + }
107 +
108 + /**
109 + * Creates a UI link identifier, with region at one end and a device/port
110 + * at the other.
111 + *
112 + * @param r region ID
113 + * @param d device ID
114 + * @param p port number
115 + */
116 + private UiLinkId(RegionId r, DeviceId d, PortNumber p) {
117 + regionA = r;
118 + elementB = d;
119 + portB = p;
120 +
121 + regionB = null;
122 + elementA = null;
123 + portA = null;
124 +
125 + idStr = r + CP_DELIMITER + elementB + ID_PORT_DELIMITER + portB;
126 + }
127 +
68 @Override 128 @Override
69 public String toString() { 129 public String toString() {
70 return idStr; 130 return idStr;
71 } 131 }
72 132
73 /** 133 /**
74 - * Returns the identifier of the first element. 134 + * Returns the identifier of the first element. Note that the returned
135 + * value will be null if this identifier is for a region-region link.
75 * 136 *
76 * @return first element identity 137 * @return first element identity
77 */ 138 */
78 public ElementId elementA() { 139 public ElementId elementA() {
79 - return idA; 140 + return elementA;
80 } 141 }
81 142
82 /** 143 /**
83 - * Returns the port of the first element. 144 + * Returns the port of the first element. Note that the returned
145 + * value will be null if this identifier is for a region-region link.
84 * 146 *
85 * @return first element port 147 * @return first element port
86 */ 148 */
...@@ -89,16 +151,18 @@ public final class UiLinkId { ...@@ -89,16 +151,18 @@ public final class UiLinkId {
89 } 151 }
90 152
91 /** 153 /**
92 - * Returns the identifier of the second element. 154 + * Returns the identifier of the second element. Note that the returned
155 + * value will be null if this identifier is for a region-region link.
93 * 156 *
94 * @return second element identity 157 * @return second element identity
95 */ 158 */
96 public ElementId elementB() { 159 public ElementId elementB() {
97 - return idB; 160 + return elementB;
98 } 161 }
99 162
100 /** 163 /**
101 - * Returns the port of the second element. 164 + * Returns the port of the second element. Note that the returned
165 + * value will be null if this identifier is for a region-region link.
102 * 166 *
103 * @return second element port 167 * @return second element port
104 */ 168 */
...@@ -106,6 +170,28 @@ public final class UiLinkId { ...@@ -106,6 +170,28 @@ public final class UiLinkId {
106 return portB; 170 return portB;
107 } 171 }
108 172
173 + /**
174 + * Returns the identity of the first region. Note that the returned value
175 + * will be null if this identifier is for a device-device or device-host
176 + * link.
177 + *
178 + * @return first region ID
179 + */
180 + public RegionId regionA() {
181 + return regionA;
182 + }
183 +
184 + /**
185 + * Returns the identity of the second region. Note that the returned value
186 + * will be null if this identifier is for a device-device or device-host
187 + * link.
188 + *
189 + * @return second region ID
190 + */
191 + public RegionId regionB() {
192 + return regionB;
193 + }
194 +
109 @Override 195 @Override
110 public boolean equals(Object o) { 196 public boolean equals(Object o) {
111 if (this == o) { 197 if (this == o) {
...@@ -134,8 +220,8 @@ public final class UiLinkId { ...@@ -134,8 +220,8 @@ public final class UiLinkId {
134 Direction directionOf(Link link) { 220 Direction directionOf(Link link) {
135 ConnectPoint src = link.src(); 221 ConnectPoint src = link.src();
136 ElementId srcId = src.elementId(); 222 ElementId srcId = src.elementId();
137 - return idA.equals(srcId) ? Direction.A_TO_B 223 + return elementA.equals(srcId) ? Direction.A_TO_B
138 - : idB.equals(srcId) ? Direction.B_TO_A 224 + : elementB.equals(srcId) ? Direction.B_TO_A
139 : null; 225 : null;
140 } 226 }
141 227
...@@ -161,4 +247,42 @@ public final class UiLinkId { ...@@ -161,4 +247,42 @@ public final class UiLinkId {
161 return comp <= 0 ? new UiLinkId(srcId, src.port(), dstId, dst.port()) 247 return comp <= 0 ? new UiLinkId(srcId, src.port(), dstId, dst.port())
162 : new UiLinkId(dstId, dst.port(), srcId, src.port()); 248 : new UiLinkId(dstId, dst.port(), srcId, src.port());
163 } 249 }
250 +
251 + /**
252 + * Generates the canonical link identifier for a link between the
253 + * specified region nodes.
254 + *
255 + * @param one the first region ID
256 + * @param two the second region ID
257 + * @return link identifier
258 + * @throws NullPointerException if any of the required fields are null
259 + * @throws IllegalArgumentException if the identifiers are identical
260 + */
261 + public static UiLinkId uiLinkId(RegionId one, RegionId two) {
262 + checkNotNull(one, E_REGION_ID_NULL);
263 + checkNotNull(two, E_REGION_ID_NULL);
264 + checkArgument(!one.equals(two), E_IDENTICAL);
265 +
266 + boolean flip = REGION_ID_COMPARATOR.compare(one, two) > 0;
267 + return flip ? new UiLinkId(two, one) : new UiLinkId(one, two);
268 + }
269 +
270 + /**
271 + * Generates the canonical link identifier for a link between the specified
272 + * region and device/port.
273 + *
274 + * @param regionId region ID
275 + * @param deviceId device ID
276 + * @param portNumber port number
277 + * @return link identifier
278 + * @throws NullPointerException if any of the required fields are null
279 + */
280 + public static UiLinkId uiLinkId(RegionId regionId, DeviceId deviceId,
281 + PortNumber portNumber) {
282 + checkNotNull(regionId, E_REGION_ID_NULL);
283 + checkNotNull(deviceId, E_DEVICE_ID_NULL);
284 + checkNotNull(portNumber, E_PORT_NULL);
285 +
286 + return new UiLinkId(regionId, deviceId, portNumber);
287 + }
164 } 288 }
......
...@@ -53,7 +53,6 @@ public class UiRegion extends UiNode { ...@@ -53,7 +53,6 @@ public class UiRegion extends UiNode {
53 // loose bindings to things in this region 53 // loose bindings to things in this region
54 private final Set<DeviceId> deviceIds = new HashSet<>(); 54 private final Set<DeviceId> deviceIds = new HashSet<>();
55 private final Set<HostId> hostIds = new HashSet<>(); 55 private final Set<HostId> hostIds = new HashSet<>();
56 - private final Set<UiLinkId> uiLinkIds = new HashSet<>();
57 56
58 private final List<String> layerOrder = new ArrayList<>(); 57 private final List<String> layerOrder = new ArrayList<>();
59 58
...@@ -84,7 +83,6 @@ public class UiRegion extends UiNode { ...@@ -84,7 +83,6 @@ public class UiRegion extends UiNode {
84 protected void destroy() { 83 protected void destroy() {
85 deviceIds.clear(); 84 deviceIds.clear();
86 hostIds.clear(); 85 hostIds.clear();
87 - uiLinkIds.clear();
88 } 86 }
89 87
90 /** 88 /**
...@@ -135,6 +133,15 @@ public class UiRegion extends UiNode { ...@@ -135,6 +133,15 @@ public class UiRegion extends UiNode {
135 } 133 }
136 134
137 /** 135 /**
136 + * Returns the UI region that is the parent of this region.
137 + *
138 + * @return the parent region
139 + */
140 + public UiRegion parentRegion() {
141 + return topology.findRegion(parent);
142 + }
143 +
144 + /**
138 * Sets the parent ID for this region. 145 * Sets the parent ID for this region.
139 * 146 *
140 * @param parentId parent ID 147 * @param parentId parent ID
...@@ -192,7 +199,6 @@ public class UiRegion extends UiNode { ...@@ -192,7 +199,6 @@ public class UiRegion extends UiNode {
192 .add("kids", kids) 199 .add("kids", kids)
193 .add("devices", deviceIds) 200 .add("devices", deviceIds)
194 .add("#hosts", hostIds.size()) 201 .add("#hosts", hostIds.size())
195 - .add("#links", uiLinkIds.size())
196 .toString(); 202 .toString();
197 } 203 }
198 204
...@@ -252,24 +258,6 @@ public class UiRegion extends UiNode { ...@@ -252,24 +258,6 @@ public class UiRegion extends UiNode {
252 } 258 }
253 259
254 /** 260 /**
255 - * Returns the set of link identifiers for this region.
256 - *
257 - * @return link identifiers for this region
258 - */
259 - public Set<UiLinkId> linkIds() {
260 - return ImmutableSet.copyOf(uiLinkIds);
261 - }
262 -
263 - /**
264 - * Returns the links in this region.
265 - *
266 - * @return the links in this region
267 - */
268 - public Set<UiLink> links() {
269 - return topology.linkSet(uiLinkIds);
270 - }
271 -
272 - /**
273 * Returns the order in which layers should be rendered. Lower layers 261 * Returns the order in which layers should be rendered. Lower layers
274 * come earlier in the list. For example, to indicate that nodes in the 262 * come earlier in the list. For example, to indicate that nodes in the
275 * optical layer should be rendered "below" nodes in the packet layer, 263 * optical layer should be rendered "below" nodes in the packet layer,
......
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.ui.model.topo;
18 +
19 +import org.onosproject.net.DeviceId;
20 +import org.onosproject.net.PortNumber;
21 +import org.onosproject.net.region.RegionId;
22 +
23 +
24 +/**
25 + * Designates a link between a region node and a device.
26 + */
27 +public class UiRegionDeviceLink extends UiLink {
28 +
29 + private static final String E_NOT_REGION_DEVICE_ID =
30 + "UI link identifier not region to device";
31 +
32 + // private (synthetic) region-device link
33 + private final RegionId region;
34 + private final DeviceId device;
35 + private final PortNumber port;
36 +
37 + /**
38 + * Creates a region to device UI link. Note that it is expected that the
39 + * link identifier is one that has a region ID at one end, and a device
40 + * ID at the other
41 + *
42 + * @param topology parent topology
43 + * @param id canonicalized link identifier
44 + * @throws IllegalArgumentException if the link ID is not region-region
45 + */
46 + public UiRegionDeviceLink(UiTopology topology, UiLinkId id) {
47 + super(topology, id);
48 + region = id.regionA();
49 + device = (DeviceId) id.elementB();
50 + port = id.portB();
51 + if (region == null || device == null || port == null) {
52 + throw new IllegalArgumentException(E_NOT_REGION_DEVICE_ID);
53 + }
54 + }
55 +
56 + @Override
57 + public String endPointA() {
58 + return region.id();
59 + }
60 +
61 + @Override
62 + public String endPointB() {
63 + return device + UiLinkId.ID_PORT_DELIMITER + port;
64 + }
65 +
66 + /**
67 + * Returns the identity of the region.
68 + *
69 + * @return region ID
70 + */
71 + public RegionId region() {
72 + return region;
73 + }
74 +
75 + /**
76 + * Returns the identity of the device.
77 + *
78 + * @return device ID
79 + */
80 + public DeviceId device() {
81 + return device;
82 + }
83 +
84 + /**
85 + * Returns the identity of the device port.
86 + *
87 + * @return device port number
88 + */
89 + public PortNumber port() {
90 + return port;
91 + }
92 +}
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.ui.model.topo;
18 +
19 +import org.onosproject.net.region.RegionId;
20 +
21 +
22 +/**
23 + * Designates a link between two region nodes.
24 + */
25 +public class UiRegionLink extends UiLink {
26 +
27 + private static final String E_NOT_REGION_ID =
28 + "UI link identifier not region to region";
29 +
30 + // private (synthetic) region - region link
31 + private final RegionId regionA;
32 + private final RegionId regionB;
33 +
34 + /**
35 + * Creates a region to region UI link. Note that it is expected that the
36 + * link identifier is one that has region IDs as source and destination.
37 + *
38 + * @param topology parent topology
39 + * @param id canonicalized link identifier
40 + * @throws IllegalArgumentException if the link ID is not region-region
41 + */
42 + public UiRegionLink(UiTopology topology, UiLinkId id) {
43 + super(topology, id);
44 + regionA = id.regionA();
45 + regionB = id.regionB();
46 + if (regionA == null || regionB == null) {
47 + throw new IllegalArgumentException(E_NOT_REGION_ID);
48 + }
49 + }
50 +
51 + @Override
52 + public String endPointA() {
53 + return regionA.id();
54 + }
55 +
56 + @Override
57 + public String endPointB() {
58 + return regionB.id();
59 + }
60 +
61 + /**
62 + * Returns the identity of the first region.
63 + *
64 + * @return first region ID
65 + */
66 + public RegionId regionA() {
67 + return regionA;
68 + }
69 +
70 + /**
71 + * Returns the identity of the second region.
72 + *
73 + * @return second region ID
74 + */
75 + public RegionId regionB() {
76 + return regionB;
77 + }
78 +}
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.ui.model.topo;
18 +
19 +import org.onosproject.net.region.RegionId;
20 +
21 +import static com.google.common.base.MoreObjects.toStringHelper;
22 +
23 +/**
24 + * A synthetic link that encapsulates a UiLink instance and the region to
25 + * which it belongs.
26 + */
27 +public class UiSynthLink {
28 +
29 + private final RegionId regionId;
30 + private final UiLink link;
31 +
32 + /**
33 + * Constructs a synthetic link with the given parameters.
34 + *
35 + * @param regionId the region to which the link belongs
36 + * @param link the link instance
37 + */
38 + public UiSynthLink(RegionId regionId, UiLink link) {
39 + this.regionId = regionId;
40 + this.link = link;
41 + }
42 +
43 + @Override
44 + public String toString() {
45 + return toStringHelper(this)
46 + .add("region", regionId)
47 + .add("link", link)
48 + .toString();
49 + }
50 +
51 + /**
52 + * Returns the region identifier.
53 + *
54 + * @return the region ID
55 + */
56 + public RegionId regionId() {
57 + return regionId;
58 + }
59 +
60 + /**
61 + * Returns the link.
62 + *
63 + * @return the link
64 + */
65 + public UiLink link() {
66 + return link;
67 + }
68 +}
...@@ -19,6 +19,7 @@ package org.onosproject.ui.model.topo; ...@@ -19,6 +19,7 @@ package org.onosproject.ui.model.topo;
19 import org.onosproject.cluster.NodeId; 19 import org.onosproject.cluster.NodeId;
20 import org.onosproject.net.DeviceId; 20 import org.onosproject.net.DeviceId;
21 import org.onosproject.net.HostId; 21 import org.onosproject.net.HostId;
22 +import org.onosproject.net.PortNumber;
22 import org.onosproject.net.region.RegionId; 23 import org.onosproject.net.region.RegionId;
23 import org.slf4j.Logger; 24 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory; 25 import org.slf4j.LoggerFactory;
...@@ -30,9 +31,12 @@ import java.util.HashMap; ...@@ -30,9 +31,12 @@ import java.util.HashMap;
30 import java.util.HashSet; 31 import java.util.HashSet;
31 import java.util.List; 32 import java.util.List;
32 import java.util.Map; 33 import java.util.Map;
34 +import java.util.Objects;
33 import java.util.Set; 35 import java.util.Set;
36 +import java.util.stream.Collectors;
34 37
35 import static com.google.common.base.MoreObjects.toStringHelper; 38 import static com.google.common.base.MoreObjects.toStringHelper;
39 +import static org.onosproject.ui.model.topo.UiLinkId.uiLinkId;
36 40
37 /** 41 /**
38 * Represents the overall network topology. 42 * Represents the overall network topology.
...@@ -59,7 +63,11 @@ public class UiTopology extends UiElement { ...@@ -59,7 +63,11 @@ public class UiTopology extends UiElement {
59 private final Map<RegionId, UiRegion> regionLookup = new HashMap<>(); 63 private final Map<RegionId, UiRegion> regionLookup = new HashMap<>();
60 private final Map<DeviceId, UiDevice> deviceLookup = new HashMap<>(); 64 private final Map<DeviceId, UiDevice> deviceLookup = new HashMap<>();
61 private final Map<HostId, UiHost> hostLookup = new HashMap<>(); 65 private final Map<HostId, UiHost> hostLookup = new HashMap<>();
62 - private final Map<UiLinkId, UiLink> linkLookup = new HashMap<>(); 66 + private final Map<UiLinkId, UiDeviceLink> devLinkLookup = new HashMap<>();
67 + private final Map<UiLinkId, UiEdgeLink> edgeLinkLookup = new HashMap<>();
68 +
69 + // a cache of the computed synthetic links
70 + private final List<UiSynthLink> synthLinks = new ArrayList<>();
63 71
64 // a container for devices, hosts, etc. belonging to no region 72 // a container for devices, hosts, etc. belonging to no region
65 private final UiRegion nullRegion = new UiRegion(this, null); 73 private final UiRegion nullRegion = new UiRegion(this, null);
...@@ -72,7 +80,9 @@ public class UiTopology extends UiElement { ...@@ -72,7 +80,9 @@ public class UiTopology extends UiElement {
72 .add("#regions", regionCount()) 80 .add("#regions", regionCount())
73 .add("#devices", deviceLookup.size()) 81 .add("#devices", deviceLookup.size())
74 .add("#hosts", hostLookup.size()) 82 .add("#hosts", hostLookup.size())
75 - .add("#links", linkLookup.size()) 83 + .add("#dev-links", devLinkLookup.size())
84 + .add("#edge-links", edgeLinkLookup.size())
85 + .add("#synth-links", synthLinks.size())
76 .toString(); 86 .toString();
77 } 87 }
78 88
...@@ -91,7 +101,10 @@ public class UiTopology extends UiElement { ...@@ -91,7 +101,10 @@ public class UiTopology extends UiElement {
91 regionLookup.clear(); 101 regionLookup.clear();
92 deviceLookup.clear(); 102 deviceLookup.clear();
93 hostLookup.clear(); 103 hostLookup.clear();
94 - linkLookup.clear(); 104 + devLinkLookup.clear();
105 + edgeLinkLookup.clear();
106 +
107 + synthLinks.clear();
95 108
96 nullRegion.destroy(); 109 nullRegion.destroy();
97 } 110 }
...@@ -261,54 +274,96 @@ public class UiTopology extends UiElement { ...@@ -261,54 +274,96 @@ public class UiTopology extends UiElement {
261 return deviceLookup.size(); 274 return deviceLookup.size();
262 } 275 }
263 276
277 +
278 + /**
279 + * Returns all device links in the model.
280 + *
281 + * @return all device links
282 + */
283 + public Set<UiDeviceLink> allDeviceLinks() {
284 + return new HashSet<>(devLinkLookup.values());
285 + }
286 +
264 /** 287 /**
265 - * Returns all links in the model. 288 + * Returns the device link with the specified identifier, or null if no
289 + * such link exists.
266 * 290 *
267 - * @return all links 291 + * @param id the canonicalized link identifier
292 + * @return corresponding UI device link
268 */ 293 */
269 - public Set<UiLink> allLinks() { 294 + public UiDeviceLink findDeviceLink(UiLinkId id) {
270 - return new HashSet<>(linkLookup.values()); 295 + return devLinkLookup.get(id);
271 } 296 }
272 297
273 /** 298 /**
274 - * Returns the link with the specified identifier, or null if no such 299 + * Returns the edge link with the specified identifier, or null if no
275 - * link exists. 300 + * such link exists.
276 * 301 *
277 * @param id the canonicalized link identifier 302 * @param id the canonicalized link identifier
278 - * @return corresponding UI link 303 + * @return corresponding UI edge link
304 + */
305 + public UiEdgeLink findEdgeLink(UiLinkId id) {
306 + return edgeLinkLookup.get(id);
307 + }
308 +
309 + /**
310 + * Adds the given UI device link to the topology model.
311 + *
312 + * @param uiDeviceLink link to add
313 + */
314 + public void add(UiDeviceLink uiDeviceLink) {
315 + devLinkLookup.put(uiDeviceLink.id(), uiDeviceLink);
316 + }
317 +
318 + /**
319 + * Adds the given UI edge link to the topology model.
320 + *
321 + * @param uiEdgeLink link to add
279 */ 322 */
280 - public UiLink findLink(UiLinkId id) { 323 + public void add(UiEdgeLink uiEdgeLink) {
281 - return linkLookup.get(id); 324 + edgeLinkLookup.put(uiEdgeLink.id(), uiEdgeLink);
282 } 325 }
283 326
284 /** 327 /**
285 - * Adds the given UI link to the topology model. 328 + * Removes the given UI device link from the model.
286 * 329 *
287 - * @param uiLink link to add 330 + * @param uiDeviceLink link to remove
288 */ 331 */
289 - public void add(UiLink uiLink) { 332 + public void remove(UiDeviceLink uiDeviceLink) {
290 - linkLookup.put(uiLink.id(), uiLink); 333 + UiDeviceLink link = devLinkLookup.remove(uiDeviceLink.id());
334 + if (link != null) {
335 + link.destroy();
336 + }
291 } 337 }
292 338
293 /** 339 /**
294 - * Removes the given UI link from the model. 340 + * Removes the given UI edge link from the model.
295 * 341 *
296 - * @param uiLink link to remove 342 + * @param uiEdgeLink link to remove
297 */ 343 */
298 - public void remove(UiLink uiLink) { 344 + public void remove(UiEdgeLink uiEdgeLink) {
299 - UiLink link = linkLookup.remove(uiLink.id()); 345 + UiEdgeLink link = edgeLinkLookup.remove(uiEdgeLink.id());
300 if (link != null) { 346 if (link != null) {
301 link.destroy(); 347 link.destroy();
302 } 348 }
303 } 349 }
304 350
305 /** 351 /**
306 - * Returns the number of links configured in the topology. 352 + * Returns the number of device links configured in the topology.
353 + *
354 + * @return number of device links
355 + */
356 + public int deviceLinkCount() {
357 + return devLinkLookup.size();
358 + }
359 +
360 + /**
361 + * Returns the number of edge links configured in the topology.
307 * 362 *
308 - * @return number of links 363 + * @return number of edge links
309 */ 364 */
310 - public int linkCount() { 365 + public int edgeLinkCount() {
311 - return linkLookup.size(); 366 + return edgeLinkLookup.size();
312 } 367 }
313 368
314 /** 369 /**
...@@ -405,22 +460,198 @@ public class UiTopology extends UiElement { ...@@ -405,22 +460,198 @@ public class UiTopology extends UiElement {
405 } 460 }
406 461
407 /** 462 /**
408 - * Returns the set of UI links with the given identifiers. 463 + * Returns the set of UI device links with the given identifiers.
409 * 464 *
410 * @param uiLinkIds link identifiers 465 * @param uiLinkIds link identifiers
411 - * @return set of matching UI link instances 466 + * @return set of matching UI device link instances
412 */ 467 */
413 - Set<UiLink> linkSet(Set<UiLinkId> uiLinkIds) { 468 + Set<UiDeviceLink> linkSet(Set<UiLinkId> uiLinkIds) {
414 - Set<UiLink> uiLinks = new HashSet<>(); 469 + Set<UiDeviceLink> result = new HashSet<>();
415 for (UiLinkId id : uiLinkIds) { 470 for (UiLinkId id : uiLinkIds) {
416 - UiLink link = linkLookup.get(id); 471 + UiDeviceLink link = devLinkLookup.get(id);
417 if (link != null) { 472 if (link != null) {
418 - uiLinks.add(link); 473 + result.add(link);
419 } else { 474 } else {
420 - log.warn(E_UNMAPPED, "link", id); 475 + log.warn(E_UNMAPPED, "device link", id);
421 } 476 }
422 } 477 }
423 - return uiLinks; 478 + return result;
479 + }
480 +
481 + /**
482 + * Uses the device-device links and data about the regions to compute the
483 + * set of synthetic links that are required per region.
484 + */
485 + public void computeSynthLinks() {
486 + List<UiSynthLink> slinks = new ArrayList<>();
487 + allDeviceLinks().forEach((link) -> {
488 + UiSynthLink synthetic = inferSyntheticLink(link);
489 + slinks.add(synthetic);
490 + log.debug("Synthetic link: {}", synthetic);
491 + });
492 +
493 + synthLinks.clear();
494 + synthLinks.addAll(slinks);
495 + }
496 +
497 + private UiSynthLink inferSyntheticLink(UiDeviceLink link) {
498 + /*
499 + Look at the containment hierarchy of each end of the link. Find the
500 + common ancestor region R. A synthetic link will be added to R, based
501 + on the "next" node back down the branch...
502 +
503 + S1 --- S2 * in the same region ...
504 + : :
505 + R R return S1 --- S2 (same link instance)
506 +
507 +
508 + S1 --- S2 * in different regions (R1, R2) at same level
509 + : :
510 + R1 R2 return R1 --- R2
511 + : :
512 + R R
513 +
514 + S1 --- S2 * in different regions at different levels
515 + : :
516 + R1 R2 return R1 --- R3
517 + : :
518 + R R3
519 + :
520 + R
521 +
522 + S1 --- S2 * in different regions at different levels
523 + : :
524 + R R2 return S1 --- R2
525 + :
526 + R
527 +
528 + */
529 + DeviceId a = link.deviceA();
530 + DeviceId b = link.deviceB();
531 + List<RegionId> aBranch = ancestors(a);
532 + List<RegionId> bBranch = ancestors(b);
533 + if (aBranch == null || bBranch == null) {
534 + return null;
535 + }
536 +
537 + return makeSynthLink(link, aBranch, bBranch);
538 + }
539 +
540 + // package private for unit testing
541 + UiSynthLink makeSynthLink(UiDeviceLink orig,
542 + List<RegionId> aBranch,
543 + List<RegionId> bBranch) {
544 +
545 + final int aSize = aBranch.size();
546 + final int bSize = bBranch.size();
547 + final int min = Math.min(aSize, bSize);
548 +
549 + int index = 0;
550 + RegionId commonRegion = aBranch.get(index);
551 +
552 + while (true) {
553 + int next = index + 1;
554 + if (next == min) {
555 + // no more pairs of regions left to test
556 + break;
557 + }
558 + RegionId rA = aBranch.get(next);
559 + RegionId rB = bBranch.get(next);
560 + if (rA.equals(rB)) {
561 + commonRegion = rA;
562 + index++;
563 + } else {
564 + break;
565 + }
566 + }
567 +
568 +
569 + int endPointIndex = index + 1;
570 + UiLinkId linkId;
571 + UiLink link;
572 +
573 + if (endPointIndex < aSize) {
574 + // the A endpoint is a subregion
575 + RegionId aRegion = aBranch.get(endPointIndex);
576 +
577 + if (endPointIndex < bSize) {
578 + // the B endpoint is a subregion
579 + RegionId bRegion = bBranch.get(endPointIndex);
580 +
581 + linkId = uiLinkId(aRegion, bRegion);
582 + link = new UiRegionLink(this, linkId);
583 +
584 + } else {
585 + // the B endpoint is the device
586 + DeviceId dB = orig.deviceB();
587 + PortNumber pB = orig.portB();
588 +
589 + linkId = uiLinkId(aRegion, dB, pB);
590 + link = new UiRegionDeviceLink(this, linkId);
591 + }
592 +
593 + } else {
594 + // the A endpoint is the device
595 + DeviceId dA = orig.deviceA();
596 + PortNumber pA = orig.portA();
597 +
598 + if (endPointIndex < bSize) {
599 + // the B endpoint is a subregion
600 + RegionId bRegion = bBranch.get(endPointIndex);
601 +
602 + linkId = uiLinkId(bRegion, dA, pA);
603 + link = new UiRegionDeviceLink(this, linkId);
604 +
605 + } else {
606 + // the B endpoint is the device
607 + // (so, we can just use the original device-device link...)
608 +
609 + link = orig;
610 + }
611 + }
612 + return new UiSynthLink(commonRegion, link);
613 + }
614 +
615 + private List<RegionId> ancestors(DeviceId id) {
616 + // return the ancestor chain from this device to root region
617 + UiDevice dev = findDevice(id);
618 + if (dev == null) {
619 + log.warn("Unable to find cached device with ID %s", id);
620 + return null;
621 + }
622 +
623 + UiRegion r = dev.uiRegion();
624 + List<RegionId> result = new ArrayList<>();
625 + while (r != null && !r.isRoot()) {
626 + result.add(0, r.id());
627 + r = r.parentRegion();
628 + }
629 + // finally add root region, since this is the grand-daddy of them all
630 + result.add(0, UiRegion.NULL_ID);
631 + return result;
632 + }
633 +
634 +
635 + /**
636 + * Returns the synthetic links associated with the specified region.
637 + *
638 + * @param regionId the region ID
639 + * @return synthetic links for this region
640 + */
641 + public List<UiSynthLink> findSynthLinks(RegionId regionId) {
642 + return synthLinks.stream()
643 + .filter(s -> Objects.equals(regionId, s.regionId()))
644 + .collect(Collectors.toList());
645 + }
646 +
647 +
648 + /**
649 + * Returns the number of synthetic links in the topology.
650 + *
651 + * @return the synthetic link count
652 + */
653 + public int synthLinkCount() {
654 + return synthLinks.size();
424 } 655 }
425 656
426 /** 657 /**
...@@ -452,13 +683,22 @@ public class UiTopology extends UiElement { ...@@ -452,13 +683,22 @@ public class UiTopology extends UiElement {
452 sb.append(INDENT_2).append(h).append(EOL); 683 sb.append(INDENT_2).append(h).append(EOL);
453 } 684 }
454 685
455 - sb.append(INDENT_1).append("Links").append(EOL); 686 + sb.append(INDENT_1).append("Device Links").append(EOL);
456 - for (UiLink link : linkLookup.values()) { 687 + for (UiLink link : devLinkLookup.values()) {
688 + sb.append(INDENT_2).append(link).append(EOL);
689 + }
690 +
691 + sb.append(INDENT_1).append("Edge Links").append(EOL);
692 + for (UiLink link : edgeLinkLookup.values()) {
693 + sb.append(INDENT_2).append(link).append(EOL);
694 + }
695 +
696 + sb.append(INDENT_1).append("Synth Links").append(EOL);
697 + for (UiSynthLink link : synthLinks) {
457 sb.append(INDENT_2).append(link).append(EOL); 698 sb.append(INDENT_2).append(link).append(EOL);
458 } 699 }
459 sb.append("------").append(EOL); 700 sb.append("------").append(EOL);
460 701
461 return sb.toString(); 702 return sb.toString();
462 } 703 }
463 -
464 } 704 }
......
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.ui.model.topo;
18 +
19 +import org.junit.Test;
20 +import org.onosproject.net.ConnectPoint;
21 +import org.onosproject.net.DefaultEdgeLink;
22 +import org.onosproject.net.DeviceId;
23 +import org.onosproject.net.EdgeLink;
24 +import org.onosproject.net.PortNumber;
25 +import org.onosproject.ui.model.AbstractUiModelTest;
26 +
27 +import static org.junit.Assert.assertEquals;
28 +
29 +/**
30 + * Unit tests for {@link UiEdgeLink}.
31 + */
32 +public class UiEdgeLinkTest extends AbstractUiModelTest {
33 +
34 + private static final String PHANTOM_HOST_ID = "00:00:00:00:00:00/None";
35 + private static final String D1_P8 = "dev-1/8";
36 +
37 + private static final DeviceId DEV = DeviceId.deviceId("dev-1");
38 + private static final PortNumber P8 = PortNumber.portNumber(8);
39 + private static final ConnectPoint CP = new ConnectPoint(DEV, P8);
40 +
41 + private static final EdgeLink EDGE_LINK =
42 + DefaultEdgeLink.createEdgeLink(CP, true);
43 +
44 + @Test
45 + public void basic() {
46 + title("basic");
47 + UiLinkId id = UiLinkId.uiLinkId(EDGE_LINK);
48 + UiEdgeLink link = new UiEdgeLink(null, id);
49 + link.attachEdgeLink(EDGE_LINK);
50 + print(link);
51 + print(link.endPointA());
52 + print(link.endPointB());
53 +
54 + assertEquals("bad end point A", PHANTOM_HOST_ID, link.endPointA());
55 + assertEquals("bad end point B", D1_P8, link.endPointB());
56 + }
57 +}
...@@ -17,17 +17,23 @@ ...@@ -17,17 +17,23 @@
17 package org.onosproject.ui.model.topo; 17 package org.onosproject.ui.model.topo;
18 18
19 import org.junit.Test; 19 import org.junit.Test;
20 +import org.onlab.packet.MacAddress;
20 import org.onosproject.net.ConnectPoint; 21 import org.onosproject.net.ConnectPoint;
21 import org.onosproject.net.DefaultLink; 22 import org.onosproject.net.DefaultLink;
22 import org.onosproject.net.DeviceId; 23 import org.onosproject.net.DeviceId;
24 +import org.onosproject.net.HostId;
23 import org.onosproject.net.Link; 25 import org.onosproject.net.Link;
24 import org.onosproject.net.PortNumber; 26 import org.onosproject.net.PortNumber;
25 import org.onosproject.net.provider.ProviderId; 27 import org.onosproject.net.provider.ProviderId;
28 +import org.onosproject.net.region.RegionId;
26 import org.onosproject.ui.model.AbstractUiModelTest; 29 import org.onosproject.ui.model.AbstractUiModelTest;
27 30
28 import static org.junit.Assert.assertEquals; 31 import static org.junit.Assert.assertEquals;
29 import static org.junit.Assert.assertNotEquals; 32 import static org.junit.Assert.assertNotEquals;
33 +import static org.junit.Assert.assertNull;
30 import static org.onosproject.net.DeviceId.deviceId; 34 import static org.onosproject.net.DeviceId.deviceId;
35 +import static org.onosproject.net.HostId.hostId;
36 +import static org.onosproject.net.PortNumber.P0;
31 import static org.onosproject.net.PortNumber.portNumber; 37 import static org.onosproject.net.PortNumber.portNumber;
32 38
33 /** 39 /**
...@@ -35,8 +41,15 @@ import static org.onosproject.net.PortNumber.portNumber; ...@@ -35,8 +41,15 @@ import static org.onosproject.net.PortNumber.portNumber;
35 */ 41 */
36 public class UiLinkIdTest extends AbstractUiModelTest { 42 public class UiLinkIdTest extends AbstractUiModelTest {
37 43
44 + private static final RegionId REG_1 = RegionId.regionId("Region-1");
45 + private static final RegionId REG_2 = RegionId.regionId("Region-2");
46 +
47 + private static final MacAddress MAC_A = MacAddress.valueOf(0x123456L);
48 + private static final HostId HOST_A = hostId(MAC_A);
49 +
38 private static final DeviceId DEV_X = deviceId("device-X"); 50 private static final DeviceId DEV_X = deviceId("device-X");
39 private static final DeviceId DEV_Y = deviceId("device-Y"); 51 private static final DeviceId DEV_Y = deviceId("device-Y");
52 +
40 private static final PortNumber P1 = portNumber(1); 53 private static final PortNumber P1 = portNumber(1);
41 private static final PortNumber P2 = portNumber(2); 54 private static final PortNumber P2 = portNumber(2);
42 private static final PortNumber P3 = portNumber(3); 55 private static final PortNumber P3 = portNumber(3);
...@@ -45,6 +58,8 @@ public class UiLinkIdTest extends AbstractUiModelTest { ...@@ -45,6 +58,8 @@ public class UiLinkIdTest extends AbstractUiModelTest {
45 private static final ConnectPoint CP_Y2 = new ConnectPoint(DEV_Y, P2); 58 private static final ConnectPoint CP_Y2 = new ConnectPoint(DEV_Y, P2);
46 private static final ConnectPoint CP_Y3 = new ConnectPoint(DEV_Y, P3); 59 private static final ConnectPoint CP_Y3 = new ConnectPoint(DEV_Y, P3);
47 60
61 + private static final ConnectPoint CP_HA = new ConnectPoint(HOST_A, P0);
62 +
48 private static final Link LINK_X1_TO_Y2 = DefaultLink.builder() 63 private static final Link LINK_X1_TO_Y2 = DefaultLink.builder()
49 .providerId(ProviderId.NONE) 64 .providerId(ProviderId.NONE)
50 .src(CP_X1) 65 .src(CP_X1)
...@@ -66,6 +81,12 @@ public class UiLinkIdTest extends AbstractUiModelTest { ...@@ -66,6 +81,12 @@ public class UiLinkIdTest extends AbstractUiModelTest {
66 .type(Link.Type.DIRECT) 81 .type(Link.Type.DIRECT)
67 .build(); 82 .build();
68 83
84 + private static final Link LINK_HA_TO_X1 = DefaultLink.builder()
85 + .providerId(ProviderId.NONE)
86 + .src(CP_HA)
87 + .dst(CP_X1)
88 + .type(Link.Type.EDGE)
89 + .build();
69 90
70 @Test 91 @Test
71 public void canonical() { 92 public void canonical() {
...@@ -86,4 +107,61 @@ public class UiLinkIdTest extends AbstractUiModelTest { ...@@ -86,4 +107,61 @@ public class UiLinkIdTest extends AbstractUiModelTest {
86 print("link other: %s", other); 107 print("link other: %s", other);
87 assertNotEquals("equiv?", one, other); 108 assertNotEquals("equiv?", one, other);
88 } 109 }
110 +
111 + @Test
112 + public void edgeLink() {
113 + title("edgeLink");
114 + UiLinkId id = UiLinkId.uiLinkId(LINK_HA_TO_X1);
115 + print("link: %s", id);
116 + assertEquals("wrong port A", P0, id.portA());
117 + assertEquals("wrong element A", HOST_A, id.elementA());
118 + assertEquals("wrong port B", P1, id.portB());
119 + assertEquals("wrong element B", DEV_X, id.elementB());
120 + assertNull("region A?", id.regionA());
121 + assertNull("region B?", id.regionB());
122 + }
123 +
124 + @Test
125 + public void deviceLink() {
126 + title("deviceLink");
127 + UiLinkId id = UiLinkId.uiLinkId(LINK_X1_TO_Y2);
128 + print("link: %s", id);
129 + assertEquals("wrong port A", P1, id.portA());
130 + assertEquals("wrong element A", DEV_X, id.elementA());
131 + assertEquals("wrong port B", P2, id.portB());
132 + assertEquals("wrong element B", DEV_Y, id.elementB());
133 + assertNull("region A?", id.regionA());
134 + assertNull("region B?", id.regionB());
135 + }
136 +
137 + @Test
138 + public void regionLink() {
139 + title("regionLink");
140 + UiLinkId idFirst = UiLinkId.uiLinkId(REG_1, REG_2);
141 + UiLinkId idSecond = UiLinkId.uiLinkId(REG_2, REG_1);
142 + print(" first: %s", idFirst);
143 + print("second: %s", idSecond);
144 + assertEquals("Not same ID", idFirst, idSecond);
145 + }
146 +
147 + @Test(expected = IllegalArgumentException.class)
148 + public void identicalRegionBad() {
149 + UiLinkId.uiLinkId(REG_1, REG_1);
150 + }
151 +
152 + @Test(expected = NullPointerException.class)
153 + public void nullRegionBad() {
154 + UiLinkId.uiLinkId(REG_1, (RegionId) null);
155 + }
156 +
157 + @Test
158 + public void regionDeviceLink() {
159 + title("regionDeviceLink");
160 + UiLinkId id = UiLinkId.uiLinkId(REG_1, DEV_X, P1);
161 + print("id: %s", id);
162 + assertEquals("region ID", REG_1, id.regionA());
163 + assertEquals("device ID", DEV_X, id.elementB());
164 + assertEquals("port", P1, id.portB());
165 + }
166 +
89 } 167 }
......
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.ui.model.topo;
18 +
19 +import org.junit.Test;
20 +import org.onosproject.net.DeviceId;
21 +import org.onosproject.net.region.RegionId;
22 +import org.onosproject.ui.model.AbstractUiModelTest;
23 +
24 +import static org.junit.Assert.assertEquals;
25 +import static org.onosproject.net.DeviceId.deviceId;
26 +import static org.onosproject.net.PortNumber.P0;
27 +import static org.onosproject.net.region.RegionId.regionId;
28 +
29 +/**
30 + * Unit tests for {@link UiRegionDeviceLink}.
31 + */
32 +public class UiRegionDeviceLinkTest extends AbstractUiModelTest {
33 +
34 + private static final RegionId R1 = regionId("r1");
35 + private static final DeviceId DEV_X = deviceId("device-X");
36 +
37 + @Test
38 + public void basic() {
39 + title("basic");
40 + UiLinkId id = UiLinkId.uiLinkId(R1, DEV_X, P0);
41 + UiRegionDeviceLink link = new UiRegionDeviceLink(null, id);
42 + print(link);
43 + assertEquals("region", R1, link.region());
44 + assertEquals("device", DEV_X, link.device());
45 + assertEquals("port", P0, link.port());
46 + }
47 +}
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.ui.model.topo;
18 +
19 +import org.junit.Test;
20 +import org.onosproject.net.ConnectPoint;
21 +import org.onosproject.net.DefaultLink;
22 +import org.onosproject.net.DeviceId;
23 +import org.onosproject.net.Link;
24 +import org.onosproject.net.provider.ProviderId;
25 +import org.onosproject.net.region.RegionId;
26 +import org.onosproject.ui.model.AbstractUiModelTest;
27 +
28 +import static org.junit.Assert.assertEquals;
29 +import static org.onosproject.net.DeviceId.deviceId;
30 +import static org.onosproject.net.PortNumber.P0;
31 +import static org.onosproject.net.region.RegionId.regionId;
32 +
33 +/**
34 + * Unit tests for {@link UiRegionLink}.
35 + */
36 +public class UiRegionLinkTest extends AbstractUiModelTest {
37 +
38 + private static final RegionId R1 = regionId("r1");
39 + private static final RegionId R2 = regionId("r2");
40 +
41 + private static final DeviceId DEV_X = deviceId("device-X");
42 + private static final DeviceId DEV_Y = deviceId("device-Y");
43 +
44 + private static final ConnectPoint CP_X = new ConnectPoint(DEV_X, P0);
45 + private static final ConnectPoint CP_Y = new ConnectPoint(DEV_Y, P0);
46 +
47 + private static final Link LINK_X_TO_Y = DefaultLink.builder()
48 + .providerId(ProviderId.NONE)
49 + .src(CP_X)
50 + .dst(CP_Y)
51 + .type(Link.Type.DIRECT)
52 + .build();
53 +
54 +
55 + @Test(expected = NullPointerException.class)
56 + public void nullPointerRegion() {
57 + title("nullPointerRegion");
58 + new UiRegionLink(null, null);
59 + }
60 +
61 + @Test
62 + public void regionToRegion() {
63 + title("regionToRegion");
64 + UiLinkId id = UiLinkId.uiLinkId(R1, R2);
65 + UiRegionLink link = new UiRegionLink(null, id);
66 + print("link: %s", link);
67 + assertEquals("bad first region", R1, link.regionA());
68 + assertEquals("bad second region", R2, link.regionB());
69 + }
70 +
71 + @Test(expected = IllegalArgumentException.class)
72 + public void wrongLinkType() {
73 + title("wrongLinkType");
74 + UiLinkId id = UiLinkId.uiLinkId(LINK_X_TO_Y);
75 + new UiRegionLink(null, id);
76 + }
77 +}
...@@ -16,20 +16,165 @@ ...@@ -16,20 +16,165 @@
16 16
17 package org.onosproject.ui.model.topo; 17 package org.onosproject.ui.model.topo;
18 18
19 +import org.junit.Before;
19 import org.junit.Test; 20 import org.junit.Test;
21 +import org.onosproject.net.ConnectPoint;
22 +import org.onosproject.net.DefaultLink;
23 +import org.onosproject.net.DeviceId;
24 +import org.onosproject.net.Link;
25 +import org.onosproject.net.PortNumber;
26 +import org.onosproject.net.provider.ProviderId;
27 +import org.onosproject.net.region.RegionId;
20 import org.onosproject.ui.AbstractUiTest; 28 import org.onosproject.ui.AbstractUiTest;
21 29
30 +import java.util.ArrayList;
31 +import java.util.Collections;
32 +import java.util.List;
33 +
34 +import static org.junit.Assert.assertEquals;
35 +import static org.onosproject.net.DeviceId.deviceId;
36 +import static org.onosproject.net.PortNumber.portNumber;
37 +
22 /** 38 /**
23 * Unit tests for {@link UiTopology}. 39 * Unit tests for {@link UiTopology}.
24 */ 40 */
25 public class UiTopologyTest extends AbstractUiTest { 41 public class UiTopologyTest extends AbstractUiTest {
26 42
43 + private static final DeviceId DEV_X = deviceId("dev-X");
44 + private static final DeviceId DEV_Y = deviceId("dev-Y");
45 + private static final PortNumber P1 = portNumber(1);
46 + private static final PortNumber P2 = portNumber(2);
47 +
48 + private static final String DEV_X_ID = "dev-x/1";
49 + private static final String DEV_Y_ID = "dev-y/2";
50 +
51 + private static final ConnectPoint CP_X1 = new ConnectPoint(DEV_X, P1);
52 + private static final ConnectPoint CP_Y2 = new ConnectPoint(DEV_Y, P2);
53 +
54 + private static final Link LINK_X1_TO_Y2 = DefaultLink.builder()
55 + .providerId(ProviderId.NONE)
56 + .src(CP_X1)
57 + .dst(CP_Y2)
58 + .type(Link.Type.DIRECT)
59 + .build();
60 +
61 + private static final UiLinkId DX1_DY2 = UiLinkId.uiLinkId(LINK_X1_TO_Y2);
62 +
63 + private static final RegionId ROOT = UiRegion.NULL_ID;
64 + private static final RegionId R1 = RegionId.regionId("R1");
65 + private static final RegionId R2 = RegionId.regionId("R2");
66 + private static final RegionId R3 = RegionId.regionId("R3");
67 +
68 + private static final String DEV_LINK_CLASS = "UiDeviceLink";
69 + private static final String REG_LINK_CLASS = "UiRegionLink";
70 + private static final String REG_DEV_LINK_CLASS = "UiRegionDeviceLink";
71 +
72 +
27 private UiTopology topo; 73 private UiTopology topo;
74 + private UiDeviceLink devLink;
75 +
76 + private List<RegionId> xBranch;
77 + private List<RegionId> yBranch;
78 + private UiSynthLink synth;
79 +
80 + @Before
81 + public void setUp() {
82 + topo = new UiTopology();
83 + devLink = new UiDeviceLink(null, DX1_DY2);
84 + devLink.attachBackingLink(LINK_X1_TO_Y2);
85 + }
28 86
29 @Test 87 @Test
30 public void basic() { 88 public void basic() {
31 title("basic"); 89 title("basic");
32 - topo = new UiTopology();
33 print(topo); 90 print(topo);
34 } 91 }
92 +
93 + private List<RegionId> branch(RegionId... ids) {
94 + List<RegionId> result = new ArrayList<>(ids.length);
95 + Collections.addAll(result, ids);
96 + return result;
97 + }
98 +
99 + private void verifySynth(RegionId id, String cls, String epA, String epB) {
100 + synth = topo.makeSynthLink(devLink, xBranch, yBranch);
101 + UiLink ulink = synth.link();
102 + print(synth);
103 + print("EpA{%s} EpB{%s}", ulink.endPointA(), ulink.endPointB());
104 +
105 + assertEquals("wrong region", id, synth.regionId());
106 + assertEquals("wrong link class", cls, ulink.type());
107 + assertEquals("wrong EP A", epA, ulink.endPointA());
108 + assertEquals("wrong EP B", epB, ulink.endPointB());
109 + }
110 +
111 + @Test
112 + public void makeSynthDevToDevRoot() {
113 + title("makeSynthDevToDevRoot");
114 + xBranch = branch(ROOT);
115 + yBranch = branch(ROOT);
116 + verifySynth(ROOT, DEV_LINK_CLASS, DEV_X_ID, DEV_Y_ID);
117 + }
118 +
119 + @Test
120 + public void makeSynthDevToDevR1() {
121 + title("makeSynthDevToDevR1");
122 + xBranch = branch(ROOT, R1);
123 + yBranch = branch(ROOT, R1);
124 + verifySynth(R1, DEV_LINK_CLASS, DEV_X_ID, DEV_Y_ID);
125 + }
126 +
127 + @Test
128 + public void makeSynthDevToDevR2() {
129 + title("makeSynthDevToDevR2");
130 + xBranch = branch(ROOT, R1, R2);
131 + yBranch = branch(ROOT, R1, R2);
132 + verifySynth(R2, DEV_LINK_CLASS, DEV_X_ID, DEV_Y_ID);
133 + }
134 +
135 + @Test
136 + public void makeSynthRegToRegRoot() {
137 + title("makeSynthRegToRegRoot");
138 + xBranch = branch(ROOT, R1);
139 + yBranch = branch(ROOT, R2);
140 + verifySynth(ROOT, REG_LINK_CLASS, R1.id(), R2.id());
141 + }
142 +
143 + @Test
144 + public void makeSynthRegToRegR1() {
145 + title("makeSynthRegToRegR1");
146 + xBranch = branch(ROOT, R1, R2);
147 + yBranch = branch(ROOT, R1, R3);
148 + verifySynth(R1, REG_LINK_CLASS, R2.id(), R3.id());
149 + }
150 +
151 + @Test
152 + public void makeSynthRegToDevRoot() {
153 + title("makeSynthRegToDevRoot");
154 +
155 + // Note: link is canonicalized to region--device order
156 +
157 + xBranch = branch(ROOT);
158 + yBranch = branch(ROOT, R1);
159 + verifySynth(ROOT, REG_DEV_LINK_CLASS, R1.id(), DEV_X_ID);
160 +
161 + xBranch = branch(ROOT, R1);
162 + yBranch = branch(ROOT);
163 + verifySynth(ROOT, REG_DEV_LINK_CLASS, R1.id(), DEV_Y_ID);
164 + }
165 +
166 + @Test
167 + public void makeSynthRegToDevR3() {
168 + title("makeSynthRegToDevR3");
169 +
170 + // Note: link is canonicalized to region--device order
171 +
172 + xBranch = branch(ROOT, R3);
173 + yBranch = branch(ROOT, R3, R1);
174 + verifySynth(R3, REG_DEV_LINK_CLASS, R1.id(), DEV_X_ID);
175 +
176 + xBranch = branch(ROOT, R3, R1);
177 + yBranch = branch(ROOT, R3);
178 + verifySynth(R3, REG_DEV_LINK_CLASS, R1.id(), DEV_Y_ID);
179 + }
35 } 180 }
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
16 16
17 package org.onosproject.ui.impl.topo; 17 package org.onosproject.ui.impl.topo;
18 18
19 +import com.fasterxml.jackson.databind.JsonNode;
19 import com.fasterxml.jackson.databind.ObjectMapper; 20 import com.fasterxml.jackson.databind.ObjectMapper;
20 import com.fasterxml.jackson.databind.node.ArrayNode; 21 import com.fasterxml.jackson.databind.node.ArrayNode;
21 import com.fasterxml.jackson.databind.node.ObjectNode; 22 import com.fasterxml.jackson.databind.node.ObjectNode;
...@@ -39,6 +40,7 @@ import org.onosproject.ui.model.topo.UiHost; ...@@ -39,6 +40,7 @@ import org.onosproject.ui.model.topo.UiHost;
39 import org.onosproject.ui.model.topo.UiLink; 40 import org.onosproject.ui.model.topo.UiLink;
40 import org.onosproject.ui.model.topo.UiNode; 41 import org.onosproject.ui.model.topo.UiNode;
41 import org.onosproject.ui.model.topo.UiRegion; 42 import org.onosproject.ui.model.topo.UiRegion;
43 +import org.onosproject.ui.model.topo.UiSynthLink;
42 import org.onosproject.ui.model.topo.UiTopoLayout; 44 import org.onosproject.ui.model.topo.UiTopoLayout;
43 import org.slf4j.Logger; 45 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory; 46 import org.slf4j.LoggerFactory;
...@@ -180,9 +182,11 @@ class Topo2Jsonifier { ...@@ -180,9 +182,11 @@ class Topo2Jsonifier {
180 * 182 *
181 * @param region the region to transform to JSON 183 * @param region the region to transform to JSON
182 * @param subRegions the subregions within this region 184 * @param subRegions the subregions within this region
185 + * @param links the links within this region
183 * @return a JSON representation of the data 186 * @return a JSON representation of the data
184 */ 187 */
185 - ObjectNode region(UiRegion region, Set<UiRegion> subRegions) { 188 + ObjectNode region(UiRegion region, Set<UiRegion> subRegions,
189 + List<UiSynthLink> links) {
186 ObjectNode payload = objectNode(); 190 ObjectNode payload = objectNode();
187 if (region == null) { 191 if (region == null) {
188 payload.put("note", "no-region"); 192 payload.put("note", "no-region");
...@@ -193,14 +197,16 @@ class Topo2Jsonifier { ...@@ -193,14 +197,16 @@ class Topo2Jsonifier {
193 payload.set("subregions", jsonSubRegions(subRegions)); 197 payload.set("subregions", jsonSubRegions(subRegions));
194 } 198 }
195 199
200 + if (links != null) {
201 + payload.set("links", jsonLinks(links));
202 + }
203 +
196 List<String> layerTags = region.layerOrder(); 204 List<String> layerTags = region.layerOrder();
197 List<Set<UiNode>> splitDevices = splitByLayer(layerTags, region.devices()); 205 List<Set<UiNode>> splitDevices = splitByLayer(layerTags, region.devices());
198 List<Set<UiNode>> splitHosts = splitByLayer(layerTags, region.hosts()); 206 List<Set<UiNode>> splitHosts = splitByLayer(layerTags, region.hosts());
199 - Set<UiLink> links = region.links();
200 207
201 payload.set("devices", jsonGrouped(splitDevices)); 208 payload.set("devices", jsonGrouped(splitDevices));
202 payload.set("hosts", jsonGrouped(splitHosts)); 209 payload.set("hosts", jsonGrouped(splitHosts));
203 - payload.set("links", jsonLinks(links));
204 payload.set("layerOrder", jsonStrings(layerTags)); 210 payload.set("layerOrder", jsonStrings(layerTags));
205 211
206 return payload; 212 return payload;
...@@ -208,24 +214,22 @@ class Topo2Jsonifier { ...@@ -208,24 +214,22 @@ class Topo2Jsonifier {
208 214
209 private ArrayNode jsonSubRegions(Set<UiRegion> subregions) { 215 private ArrayNode jsonSubRegions(Set<UiRegion> subregions) {
210 ArrayNode kids = arrayNode(); 216 ArrayNode kids = arrayNode();
211 - if (subregions != null) {
212 subregions.forEach(s -> kids.add(jsonClosedRegion(s))); 217 subregions.forEach(s -> kids.add(jsonClosedRegion(s)));
213 - }
214 return kids; 218 return kids;
215 } 219 }
216 220
221 + private JsonNode jsonLinks(List<UiSynthLink> links) {
222 + ArrayNode synthLinks = arrayNode();
223 + links.forEach(l -> synthLinks.add(json(l)));
224 + return synthLinks;
225 + }
226 +
217 private ArrayNode jsonStrings(List<String> strings) { 227 private ArrayNode jsonStrings(List<String> strings) {
218 ArrayNode array = arrayNode(); 228 ArrayNode array = arrayNode();
219 strings.forEach(array::add); 229 strings.forEach(array::add);
220 return array; 230 return array;
221 } 231 }
222 232
223 - private ArrayNode jsonLinks(Set<UiLink> links) {
224 - ArrayNode result = arrayNode();
225 - links.forEach(lnk -> result.add(json(lnk)));
226 - return result;
227 - }
228 -
229 private ArrayNode jsonGrouped(List<Set<UiNode>> groupedNodes) { 233 private ArrayNode jsonGrouped(List<Set<UiNode>> groupedNodes) {
230 ArrayNode result = arrayNode(); 234 ArrayNode result = arrayNode();
231 groupedNodes.forEach(g -> { 235 groupedNodes.forEach(g -> {
...@@ -280,11 +284,13 @@ class Topo2Jsonifier { ...@@ -280,11 +284,13 @@ class Topo2Jsonifier {
280 // TODO: complete host details 284 // TODO: complete host details
281 } 285 }
282 286
283 - 287 + private ObjectNode json(UiSynthLink sLink) {
284 - private ObjectNode json(UiLink link) { 288 + UiLink uLink = sLink.link();
285 return objectNode() 289 return objectNode()
286 - .put("id", link.idAsString()); 290 + .put("id", uLink.idAsString())
287 - // TODO: complete link details 291 + .put("epA", uLink.endPointA())
292 + .put("epB", uLink.endPointB())
293 + .put("type", uLink.type());
288 } 294 }
289 295
290 296
...@@ -305,7 +311,7 @@ class Topo2Jsonifier { ...@@ -305,7 +311,7 @@ class Topo2Jsonifier {
305 */ 311 */
306 public ArrayNode closedNodes(Set<UiNode> nodes) { 312 public ArrayNode closedNodes(Set<UiNode> nodes) {
307 ArrayNode array = arrayNode(); 313 ArrayNode array = arrayNode();
308 - for (UiNode node: nodes) { 314 + for (UiNode node : nodes) {
309 if (node instanceof UiRegion) { 315 if (node instanceof UiRegion) {
310 array.add(jsonClosedRegion((UiRegion) node)); 316 array.add(jsonClosedRegion((UiRegion) node));
311 } else if (node instanceof UiDevice) { 317 } else if (node instanceof UiDevice) {
...@@ -361,20 +367,6 @@ class Topo2Jsonifier { ...@@ -361,20 +367,6 @@ class Topo2Jsonifier {
361 return array; 367 return array;
362 } 368 }
363 369
364 - /**
365 - * Returns a JSON array representation of a list of links.
366 - *
367 - * @param links the links
368 - * @return a JSON representation of the links
369 - */
370 - public ArrayNode links(Set<UiLink> links) {
371 - ArrayNode array = arrayNode();
372 - for (UiLink link : links) {
373 - array.add(json(link));
374 - }
375 - return array;
376 - }
377 -
378 // package-private for unit testing 370 // package-private for unit testing
379 List<Set<UiNode>> splitByLayer(List<String> layerTags, 371 List<Set<UiNode>> splitByLayer(List<String> layerTags,
380 Set<? extends UiNode> nodes) { 372 Set<? extends UiNode> nodes) {
......
...@@ -26,6 +26,7 @@ import org.onosproject.ui.impl.UiWebSocket; ...@@ -26,6 +26,7 @@ import org.onosproject.ui.impl.UiWebSocket;
26 import org.onosproject.ui.model.topo.UiClusterMember; 26 import org.onosproject.ui.model.topo.UiClusterMember;
27 import org.onosproject.ui.model.topo.UiNode; 27 import org.onosproject.ui.model.topo.UiNode;
28 import org.onosproject.ui.model.topo.UiRegion; 28 import org.onosproject.ui.model.topo.UiRegion;
29 +import org.onosproject.ui.model.topo.UiSynthLink;
29 import org.onosproject.ui.model.topo.UiTopoLayout; 30 import org.onosproject.ui.model.topo.UiTopoLayout;
30 import org.slf4j.Logger; 31 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory; 32 import org.slf4j.LoggerFactory;
...@@ -125,7 +126,8 @@ public class Topo2ViewMessageHandler extends UiMessageHandler { ...@@ -125,7 +126,8 @@ public class Topo2ViewMessageHandler extends UiMessageHandler {
125 // (as well as layer-order hints) 126 // (as well as layer-order hints)
126 UiRegion region = topoSession.getRegion(currentLayout); 127 UiRegion region = topoSession.getRegion(currentLayout);
127 Set<UiRegion> kids = topoSession.getSubRegions(currentLayout); 128 Set<UiRegion> kids = topoSession.getSubRegions(currentLayout);
128 - sendMessage(CURRENT_REGION, t2json.region(region, kids)); 129 + List<UiSynthLink> links = topoSession.getLinks(currentLayout);
130 + sendMessage(CURRENT_REGION, t2json.region(region, kids, links));
129 131
130 // these are the regions/devices that are siblings to this region 132 // these are the regions/devices that are siblings to this region
131 Set<UiNode> peers = topoSession.getPeerNodes(currentLayout); 133 Set<UiNode> peers = topoSession.getPeerNodes(currentLayout);
...@@ -133,6 +135,8 @@ public class Topo2ViewMessageHandler extends UiMessageHandler { ...@@ -133,6 +135,8 @@ public class Topo2ViewMessageHandler extends UiMessageHandler {
133 peersPayload.set("peers", t2json.closedNodes(peers)); 135 peersPayload.set("peers", t2json.closedNodes(peers));
134 sendMessage(PEER_REGIONS, peersPayload); 136 sendMessage(PEER_REGIONS, peersPayload);
135 137
138 + // TODO: send breadcrumb message
139 +
136 // finally, tell the UI that we are done : TODO review / delete?? 140 // finally, tell the UI that we are done : TODO review / delete??
137 sendMessage(TOPO_START_DONE, null); 141 sendMessage(TOPO_START_DONE, null);
138 142
......
...@@ -25,6 +25,7 @@ import org.onosproject.ui.impl.topo.model.UiSharedTopologyModel; ...@@ -25,6 +25,7 @@ import org.onosproject.ui.impl.topo.model.UiSharedTopologyModel;
25 import org.onosproject.ui.model.topo.UiClusterMember; 25 import org.onosproject.ui.model.topo.UiClusterMember;
26 import org.onosproject.ui.model.topo.UiNode; 26 import org.onosproject.ui.model.topo.UiNode;
27 import org.onosproject.ui.model.topo.UiRegion; 27 import org.onosproject.ui.model.topo.UiRegion;
28 +import org.onosproject.ui.model.topo.UiSynthLink;
28 import org.onosproject.ui.model.topo.UiTopoLayout; 29 import org.onosproject.ui.model.topo.UiTopoLayout;
29 import org.slf4j.Logger; 30 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory; 31 import org.slf4j.LoggerFactory;
...@@ -214,6 +215,16 @@ public class UiTopoSession implements UiModelListener { ...@@ -214,6 +215,16 @@ public class UiTopoSession implements UiModelListener {
214 } 215 }
215 216
216 /** 217 /**
218 + * Returns the (synthetic) links of the region in the specified layout.
219 + *
220 + * @param layout the layout being viewed
221 + * @return all links that are contained by this layout's region
222 + */
223 + public List<UiSynthLink> getLinks(UiTopoLayout layout) {
224 + return sharedModel.getSynthLinks(layout.regionId());
225 + }
226 +
227 + /**
217 * Refreshes the model's internal state. 228 * Refreshes the model's internal state.
218 */ 229 */
219 public void refreshModel() { 230 public void refreshModel() {
......
...@@ -29,6 +29,6 @@ public class ListLinks extends AbstractElementCommand { ...@@ -29,6 +29,6 @@ public class ListLinks extends AbstractElementCommand {
29 @Override 29 @Override
30 protected void execute() { 30 protected void execute() {
31 UiSharedTopologyModel model = get(UiSharedTopologyModel.class); 31 UiSharedTopologyModel model = get(UiSharedTopologyModel.class);
32 - sorted(model.getLinks()).forEach(l -> print("%s", l)); 32 + sorted(model.getDeviceLinks()).forEach(l -> print("%s", l));
33 } 33 }
34 } 34 }
......
...@@ -33,11 +33,13 @@ import org.onosproject.ui.UiTopoLayoutService; ...@@ -33,11 +33,13 @@ import org.onosproject.ui.UiTopoLayoutService;
33 import org.onosproject.ui.model.ServiceBundle; 33 import org.onosproject.ui.model.ServiceBundle;
34 import org.onosproject.ui.model.topo.UiClusterMember; 34 import org.onosproject.ui.model.topo.UiClusterMember;
35 import org.onosproject.ui.model.topo.UiDevice; 35 import org.onosproject.ui.model.topo.UiDevice;
36 +import org.onosproject.ui.model.topo.UiDeviceLink;
37 +import org.onosproject.ui.model.topo.UiEdgeLink;
36 import org.onosproject.ui.model.topo.UiElement; 38 import org.onosproject.ui.model.topo.UiElement;
37 import org.onosproject.ui.model.topo.UiHost; 39 import org.onosproject.ui.model.topo.UiHost;
38 -import org.onosproject.ui.model.topo.UiLink;
39 import org.onosproject.ui.model.topo.UiLinkId; 40 import org.onosproject.ui.model.topo.UiLinkId;
40 import org.onosproject.ui.model.topo.UiRegion; 41 import org.onosproject.ui.model.topo.UiRegion;
42 +import org.onosproject.ui.model.topo.UiSynthLink;
41 import org.onosproject.ui.model.topo.UiTopoLayout; 43 import org.onosproject.ui.model.topo.UiTopoLayout;
42 import org.onosproject.ui.model.topo.UiTopoLayoutId; 44 import org.onosproject.ui.model.topo.UiTopoLayoutId;
43 import org.onosproject.ui.model.topo.UiTopology; 45 import org.onosproject.ui.model.topo.UiTopology;
...@@ -101,7 +103,7 @@ class ModelCache { ...@@ -101,7 +103,7 @@ class ModelCache {
101 loadClusterMembers(); 103 loadClusterMembers();
102 loadRegions(); 104 loadRegions();
103 loadDevices(); 105 loadDevices();
104 - loadLinks(); 106 + loadDeviceLinks();
105 loadHosts(); 107 loadHosts();
106 } 108 }
107 109
...@@ -334,66 +336,72 @@ class ModelCache { ...@@ -334,66 +336,72 @@ class ModelCache {
334 } 336 }
335 337
336 338
337 - // === LINKS 339 + // === LINKS ===
338 340
339 - private UiLink addNewLink(UiLinkId id) { 341 + private UiDeviceLink addNewDeviceLink(UiLinkId id) {
340 - UiLink uiLink = new UiLink(uiTopology, id); 342 + UiDeviceLink uiDeviceLink = new UiDeviceLink(uiTopology, id);
341 - uiTopology.add(uiLink); 343 + uiTopology.add(uiDeviceLink);
342 - return uiLink; 344 + return uiDeviceLink;
343 } 345 }
344 346
345 - private void updateLink(UiLink uiLink, Link link) { 347 + private UiEdgeLink addNewEdgeLink(UiLinkId id) {
346 - uiLink.attachBackingLink(link); 348 + UiEdgeLink uiEdgeLink = new UiEdgeLink(uiTopology, id);
349 + uiTopology.add(uiEdgeLink);
350 + return uiEdgeLink;
347 } 351 }
348 352
349 - private void loadLinks() { 353 + private void updateDeviceLink(UiDeviceLink uiDeviceLink, Link link) {
354 + uiDeviceLink.attachBackingLink(link);
355 + }
356 +
357 + private void loadDeviceLinks() {
350 for (Link link : services.link().getLinks()) { 358 for (Link link : services.link().getLinks()) {
351 UiLinkId id = uiLinkId(link); 359 UiLinkId id = uiLinkId(link);
352 360
353 - UiLink uiLink = uiTopology.findLink(id); 361 + UiDeviceLink uiDeviceLink = uiTopology.findDeviceLink(id);
354 - if (uiLink == null) { 362 + if (uiDeviceLink == null) {
355 - uiLink = addNewLink(id); 363 + uiDeviceLink = addNewDeviceLink(id);
356 } 364 }
357 - updateLink(uiLink, link); 365 + updateDeviceLink(uiDeviceLink, link);
358 } 366 }
359 } 367 }
360 368
361 // invoked from UiSharedTopologyModel link listener 369 // invoked from UiSharedTopologyModel link listener
362 - void addOrUpdateLink(Link link) { 370 + void addOrUpdateDeviceLink(Link link) {
363 UiLinkId id = uiLinkId(link); 371 UiLinkId id = uiLinkId(link);
364 - UiLink uiLink = uiTopology.findLink(id); 372 + UiDeviceLink uiDeviceLink = uiTopology.findDeviceLink(id);
365 - if (uiLink == null) { 373 + if (uiDeviceLink == null) {
366 - uiLink = addNewLink(id); 374 + uiDeviceLink = addNewDeviceLink(id);
367 } 375 }
368 - updateLink(uiLink, link); 376 + updateDeviceLink(uiDeviceLink, link);
369 377
370 - postEvent(LINK_ADDED_OR_UPDATED, uiLink); 378 + postEvent(LINK_ADDED_OR_UPDATED, uiDeviceLink);
371 } 379 }
372 380
373 // package private for unit test access 381 // package private for unit test access
374 - UiLink accessLink(UiLinkId id) { 382 + UiDeviceLink accessDeviceLink(UiLinkId id) {
375 - return uiTopology.findLink(id); 383 + return uiTopology.findDeviceLink(id);
376 } 384 }
377 385
378 // invoked from UiSharedTopologyModel link listener 386 // invoked from UiSharedTopologyModel link listener
379 - void removeLink(Link link) { 387 + void removeDeviceLink(Link link) {
380 UiLinkId id = uiLinkId(link); 388 UiLinkId id = uiLinkId(link);
381 - UiLink uiLink = uiTopology.findLink(id); 389 + UiDeviceLink uiDeviceLink = uiTopology.findDeviceLink(id);
382 - if (uiLink != null) { 390 + if (uiDeviceLink != null) {
383 - boolean remaining = uiLink.detachBackingLink(link); 391 + boolean remaining = uiDeviceLink.detachBackingLink(link);
384 if (remaining) { 392 if (remaining) {
385 - postEvent(LINK_ADDED_OR_UPDATED, uiLink); 393 + postEvent(LINK_ADDED_OR_UPDATED, uiDeviceLink);
386 } else { 394 } else {
387 - uiTopology.remove(uiLink); 395 + uiTopology.remove(uiDeviceLink);
388 - postEvent(LINK_REMOVED, uiLink); 396 + postEvent(LINK_REMOVED, uiDeviceLink);
389 } 397 }
390 } else { 398 } else {
391 - log.warn(E_NO_ELEMENT, "link", id); 399 + log.warn(E_NO_ELEMENT, "Device link", id);
392 } 400 }
393 } 401 }
394 402
395 - Set<UiLink> getAllLinks() { 403 + Set<UiDeviceLink> getAllDeviceLinks() {
396 - return uiTopology.allLinks(); 404 + return uiTopology.allDeviceLinks();
397 } 405 }
398 406
399 // === HOSTS 407 // === HOSTS
...@@ -411,20 +419,19 @@ class ModelCache { ...@@ -411,20 +419,19 @@ class ModelCache {
411 host.setEdgeLinkId(elinkId); 419 host.setEdgeLinkId(elinkId);
412 420
413 // add synthesized edge link to the topology 421 // add synthesized edge link to the topology
414 - UiLink edgeLink = addNewLink(elinkId); 422 + UiEdgeLink edgeLink = addNewEdgeLink(elinkId);
415 edgeLink.attachEdgeLink(elink); 423 edgeLink.attachEdgeLink(elink);
416 424
417 return host; 425 return host;
418 } 426 }
419 427
420 - private void insertNewUiLink(UiLinkId id, EdgeLink e) { 428 + private void insertNewUiEdgeLink(UiLinkId id, EdgeLink e) {
421 - UiLink newEdgeLink = addNewLink(id); 429 + UiEdgeLink newEdgeLink = addNewEdgeLink(id);
422 newEdgeLink.attachEdgeLink(e); 430 newEdgeLink.attachEdgeLink(e);
423 -
424 } 431 }
425 432
426 private void updateHost(UiHost uiHost, Host h) { 433 private void updateHost(UiHost uiHost, Host h) {
427 - UiLink existing = uiTopology.findLink(uiHost.edgeLinkId()); 434 + UiEdgeLink existing = uiTopology.findEdgeLink(uiHost.edgeLinkId());
428 435
429 EdgeLink currentElink = synthesizeLink(h); 436 EdgeLink currentElink = synthesizeLink(h);
430 UiLinkId currentElinkId = uiLinkId(currentElink); 437 UiLinkId currentElinkId = uiLinkId(currentElink);
...@@ -432,7 +439,7 @@ class ModelCache { ...@@ -432,7 +439,7 @@ class ModelCache {
432 if (existing != null) { 439 if (existing != null) {
433 if (!currentElinkId.equals(existing.id())) { 440 if (!currentElinkId.equals(existing.id())) {
434 // edge link has changed 441 // edge link has changed
435 - insertNewUiLink(currentElinkId, currentElink); 442 + insertNewUiEdgeLink(currentElinkId, currentElink);
436 uiHost.setEdgeLinkId(currentElinkId); 443 uiHost.setEdgeLinkId(currentElinkId);
437 444
438 uiTopology.remove(existing); 445 uiTopology.remove(existing);
...@@ -440,7 +447,7 @@ class ModelCache { ...@@ -440,7 +447,7 @@ class ModelCache {
440 447
441 } else { 448 } else {
442 // no previously existing edge link 449 // no previously existing edge link
443 - insertNewUiLink(currentElinkId, currentElink); 450 + insertNewUiEdgeLink(currentElinkId, currentElink);
444 uiHost.setEdgeLinkId(currentElinkId); 451 uiHost.setEdgeLinkId(currentElinkId);
445 452
446 } 453 }
...@@ -489,7 +496,7 @@ class ModelCache { ...@@ -489,7 +496,7 @@ class ModelCache {
489 HostId id = host.id(); 496 HostId id = host.id();
490 UiHost uiHost = uiTopology.findHost(id); 497 UiHost uiHost = uiTopology.findHost(id);
491 if (uiHost != null) { 498 if (uiHost != null) {
492 - UiLink edgeLink = uiTopology.findLink(uiHost.edgeLinkId()); 499 + UiEdgeLink edgeLink = uiTopology.findEdgeLink(uiHost.edgeLinkId());
493 uiTopology.remove(edgeLink); 500 uiTopology.remove(edgeLink);
494 uiTopology.remove(uiHost); 501 uiTopology.remove(uiHost);
495 postEvent(HOST_REMOVED, uiHost); 502 postEvent(HOST_REMOVED, uiHost);
...@@ -503,11 +510,17 @@ class ModelCache { ...@@ -503,11 +510,17 @@ class ModelCache {
503 } 510 }
504 511
505 512
513 + // === SYNTHETIC LINKS
514 +
515 + List<UiSynthLink> getSynthLinks(RegionId regionId) {
516 + return uiTopology.findSynthLinks(regionId);
517 + }
518 +
506 /** 519 /**
507 * Refreshes the internal state. 520 * Refreshes the internal state.
508 */ 521 */
509 public void refresh() { 522 public void refresh() {
510 - // fix up internal linkages if they aren't correct 523 + // fix up internal linkages to ensure they are correct
511 524
512 // make sure regions reflect layout containment hierarchy 525 // make sure regions reflect layout containment hierarchy
513 fixupContainmentHierarchy(uiTopology.nullRegion()); 526 fixupContainmentHierarchy(uiTopology.nullRegion());
...@@ -542,8 +555,13 @@ class ModelCache { ...@@ -542,8 +555,13 @@ class ModelCache {
542 Set<DeviceId> leftOver = new HashSet<>(allDevices.size()); 555 Set<DeviceId> leftOver = new HashSet<>(allDevices.size());
543 allDevices.forEach(d -> leftOver.add(d.id())); 556 allDevices.forEach(d -> leftOver.add(d.id()));
544 uiTopology.nullRegion().reconcileDevices(leftOver); 557 uiTopology.nullRegion().reconcileDevices(leftOver);
558 +
559 + // now that we have correct region hierarchy, and devices are in their
560 + // respective regions, we can compute synthetic links for each region.
561 + uiTopology.computeSynthLinks();
545 } 562 }
546 563
564 +
547 // === CACHE STATISTICS 565 // === CACHE STATISTICS
548 566
549 /** 567 /**
...@@ -583,12 +601,21 @@ class ModelCache { ...@@ -583,12 +601,21 @@ class ModelCache {
583 } 601 }
584 602
585 /** 603 /**
586 - * Returns the number of links in the topology. 604 + * Returns the number of device links in the topology.
587 * 605 *
588 - * @return number of links 606 + * @return number of device links
589 */ 607 */
590 - public int linkCount() { 608 + public int deviceLinkCount() {
591 - return uiTopology.linkCount(); 609 + return uiTopology.deviceLinkCount();
610 + }
611 +
612 + /**
613 + * Returns the number of edge links in the topology.
614 + *
615 + * @return number of edge links
616 + */
617 + public int edgeLinkCount() {
618 + return uiTopology.edgeLinkCount();
592 } 619 }
593 620
594 /** 621 /**
...@@ -600,4 +627,12 @@ class ModelCache { ...@@ -600,4 +627,12 @@ class ModelCache {
600 return uiTopology.hostCount(); 627 return uiTopology.hostCount();
601 } 628 }
602 629
630 + /**
631 + * Returns the number of synthetic links in the topology.
632 + *
633 + * @return the number of synthetic links
634 + */
635 + public int synthLinkCount() {
636 + return uiTopology.synthLinkCount();
637 + }
603 } 638 }
......
...@@ -65,9 +65,10 @@ import org.onosproject.ui.impl.topo.UiTopoSession; ...@@ -65,9 +65,10 @@ import org.onosproject.ui.impl.topo.UiTopoSession;
65 import org.onosproject.ui.model.ServiceBundle; 65 import org.onosproject.ui.model.ServiceBundle;
66 import org.onosproject.ui.model.topo.UiClusterMember; 66 import org.onosproject.ui.model.topo.UiClusterMember;
67 import org.onosproject.ui.model.topo.UiDevice; 67 import org.onosproject.ui.model.topo.UiDevice;
68 +import org.onosproject.ui.model.topo.UiDeviceLink;
68 import org.onosproject.ui.model.topo.UiHost; 69 import org.onosproject.ui.model.topo.UiHost;
69 -import org.onosproject.ui.model.topo.UiLink;
70 import org.onosproject.ui.model.topo.UiRegion; 70 import org.onosproject.ui.model.topo.UiRegion;
71 +import org.onosproject.ui.model.topo.UiSynthLink;
71 import org.slf4j.Logger; 72 import org.slf4j.Logger;
72 import org.slf4j.LoggerFactory; 73 import org.slf4j.LoggerFactory;
73 74
...@@ -269,12 +270,22 @@ public final class UiSharedTopologyModel ...@@ -269,12 +270,22 @@ public final class UiSharedTopologyModel
269 } 270 }
270 271
271 /** 272 /**
272 - * Returns the set of links stored in the model cache. 273 + * Returns the set of device links stored in the model cache.
273 * 274 *
274 - * @return set of links 275 + * @return set of device links
275 */ 276 */
276 - public Set<UiLink> getLinks() { 277 + public Set<UiDeviceLink> getDeviceLinks() {
277 - return cache.getAllLinks(); 278 + return cache.getAllDeviceLinks();
279 + }
280 +
281 + /**
282 + * Returns the synthetic links associated with the specified region.
283 + *
284 + * @param regionId region ID
285 + * @return synthetic links for that region
286 + */
287 + public List<UiSynthLink> getSynthLinks(RegionId regionId) {
288 + return cache.getSynthLinks(regionId);
278 } 289 }
279 290
280 // ===================================================================== 291 // =====================================================================
...@@ -434,11 +445,11 @@ public final class UiSharedTopologyModel ...@@ -434,11 +445,11 @@ public final class UiSharedTopologyModel
434 445
435 case LINK_ADDED: 446 case LINK_ADDED:
436 case LINK_UPDATED: 447 case LINK_UPDATED:
437 - cache.addOrUpdateLink(link); 448 + cache.addOrUpdateDeviceLink(link);
438 break; 449 break;
439 450
440 case LINK_REMOVED: 451 case LINK_REMOVED:
441 - cache.removeLink(link); 452 + cache.removeDeviceLink(link);
442 break; 453 break;
443 454
444 default: 455 default:
......
...@@ -29,9 +29,9 @@ import org.onosproject.net.region.Region; ...@@ -29,9 +29,9 @@ import org.onosproject.net.region.Region;
29 import org.onosproject.ui.impl.topo.model.UiModelEvent.Type; 29 import org.onosproject.ui.impl.topo.model.UiModelEvent.Type;
30 import org.onosproject.ui.model.topo.UiClusterMember; 30 import org.onosproject.ui.model.topo.UiClusterMember;
31 import org.onosproject.ui.model.topo.UiDevice; 31 import org.onosproject.ui.model.topo.UiDevice;
32 +import org.onosproject.ui.model.topo.UiDeviceLink;
32 import org.onosproject.ui.model.topo.UiElement; 33 import org.onosproject.ui.model.topo.UiElement;
33 import org.onosproject.ui.model.topo.UiHost; 34 import org.onosproject.ui.model.topo.UiHost;
34 -import org.onosproject.ui.model.topo.UiLink;
35 import org.onosproject.ui.model.topo.UiLinkId; 35 import org.onosproject.ui.model.topo.UiLinkId;
36 import org.onosproject.ui.model.topo.UiRegion; 36 import org.onosproject.ui.model.topo.UiRegion;
37 37
...@@ -265,54 +265,54 @@ public class ModelCacheTest extends AbstractTopoModelTest { ...@@ -265,54 +265,54 @@ public class ModelCacheTest extends AbstractTopoModelTest {
265 // we've established that the ID is the same for both 265 // we've established that the ID is the same for both
266 UiLinkId linkId = idA2B; 266 UiLinkId linkId = idA2B;
267 267
268 - cache.addOrUpdateLink(link1); 268 + cache.addOrUpdateDeviceLink(link1);
269 dispatcher.assertLast(Type.LINK_ADDED_OR_UPDATED, linkId.toString()); 269 dispatcher.assertLast(Type.LINK_ADDED_OR_UPDATED, linkId.toString());
270 dispatcher.assertEventCount(1); 270 dispatcher.assertEventCount(1);
271 - assertEquals("unex # links", 1, cache.linkCount()); 271 + assertEquals("unex # links", 1, cache.deviceLinkCount());
272 272
273 - UiLink link = cache.accessLink(linkId); 273 + UiDeviceLink link = cache.accessDeviceLink(linkId);
274 assertEquals("dev A not d2", DEVID_2, link.deviceA()); 274 assertEquals("dev A not d2", DEVID_2, link.deviceA());
275 assertEquals("dev B not d7", DEVID_7, link.deviceB()); 275 assertEquals("dev B not d7", DEVID_7, link.deviceB());
276 assertEquals("wrong backing link A-B", link1, link.linkAtoB()); 276 assertEquals("wrong backing link A-B", link1, link.linkAtoB());
277 assertEquals("backing link B-A?", null, link.linkBtoA()); 277 assertEquals("backing link B-A?", null, link.linkBtoA());
278 278
279 - cache.addOrUpdateLink(link2); 279 + cache.addOrUpdateDeviceLink(link2);
280 dispatcher.assertLast(Type.LINK_ADDED_OR_UPDATED, linkId.toString()); 280 dispatcher.assertLast(Type.LINK_ADDED_OR_UPDATED, linkId.toString());
281 dispatcher.assertEventCount(2); 281 dispatcher.assertEventCount(2);
282 // NOTE: yes! expect 1 UiLink 282 // NOTE: yes! expect 1 UiLink
283 - assertEquals("unex # links", 1, cache.linkCount()); 283 + assertEquals("unex # links", 1, cache.deviceLinkCount());
284 284
285 - link = cache.accessLink(linkId); 285 + link = cache.accessDeviceLink(linkId);
286 assertEquals("dev A not d2", DEVID_2, link.deviceA()); 286 assertEquals("dev A not d2", DEVID_2, link.deviceA());
287 assertEquals("dev B not d7", DEVID_7, link.deviceB()); 287 assertEquals("dev B not d7", DEVID_7, link.deviceB());
288 assertEquals("wrong backing link A-B", link1, link.linkAtoB()); 288 assertEquals("wrong backing link A-B", link1, link.linkAtoB());
289 assertEquals("wrong backing link B-A", link2, link.linkBtoA()); 289 assertEquals("wrong backing link B-A", link2, link.linkBtoA());
290 290
291 // now remove links one at a time 291 // now remove links one at a time
292 - cache.removeLink(link1); 292 + cache.removeDeviceLink(link1);
293 // NOTE: yes! ADD_OR_UPDATE, since the link was updated 293 // NOTE: yes! ADD_OR_UPDATE, since the link was updated
294 dispatcher.assertLast(Type.LINK_ADDED_OR_UPDATED, linkId.toString()); 294 dispatcher.assertLast(Type.LINK_ADDED_OR_UPDATED, linkId.toString());
295 dispatcher.assertEventCount(3); 295 dispatcher.assertEventCount(3);
296 // NOTE: yes! expect 1 UiLink (still) 296 // NOTE: yes! expect 1 UiLink (still)
297 - assertEquals("unex # links", 1, cache.linkCount()); 297 + assertEquals("unex # links", 1, cache.deviceLinkCount());
298 298
299 - link = cache.accessLink(linkId); 299 + link = cache.accessDeviceLink(linkId);
300 assertEquals("dev A not d2", DEVID_2, link.deviceA()); 300 assertEquals("dev A not d2", DEVID_2, link.deviceA());
301 assertEquals("dev B not d7", DEVID_7, link.deviceB()); 301 assertEquals("dev B not d7", DEVID_7, link.deviceB());
302 assertEquals("backing link A-B?", null, link.linkAtoB()); 302 assertEquals("backing link A-B?", null, link.linkAtoB());
303 assertEquals("wrong backing link B-A", link2, link.linkBtoA()); 303 assertEquals("wrong backing link B-A", link2, link.linkBtoA());
304 304
305 // remove final link 305 // remove final link
306 - cache.removeLink(link2); 306 + cache.removeDeviceLink(link2);
307 dispatcher.assertLast(Type.LINK_REMOVED, linkId.toString()); 307 dispatcher.assertLast(Type.LINK_REMOVED, linkId.toString());
308 dispatcher.assertEventCount(4); 308 dispatcher.assertEventCount(4);
309 // NOTE: finally link should be removed from cache 309 // NOTE: finally link should be removed from cache
310 - assertEquals("unex # links", 0, cache.linkCount()); 310 + assertEquals("unex # links", 0, cache.deviceLinkCount());
311 } 311 }
312 312
313 private void assertHostLinkCounts(int nHosts, int nLinks) { 313 private void assertHostLinkCounts(int nHosts, int nLinks) {
314 assertEquals("unex # hosts", nHosts, cache.hostCount()); 314 assertEquals("unex # hosts", nHosts, cache.hostCount());
315 - assertEquals("unex # links", nLinks, cache.linkCount()); 315 + assertEquals("unex # links", nLinks, cache.edgeLinkCount());
316 } 316 }
317 317
318 private void assertLocation(HostId hid, DeviceId expDev, int expPort) { 318 private void assertLocation(HostId hid, DeviceId expDev, int expPort) {
...@@ -403,6 +403,8 @@ public class ModelCacheTest extends AbstractTopoModelTest { ...@@ -403,6 +403,8 @@ public class ModelCacheTest extends AbstractTopoModelTest {
403 assertEquals("unex # regions", 3, cache.regionCount()); 403 assertEquals("unex # regions", 3, cache.regionCount());
404 assertEquals("unex # devices", 9, cache.deviceCount()); 404 assertEquals("unex # devices", 9, cache.deviceCount());
405 assertEquals("unex # hosts", 18, cache.hostCount()); 405 assertEquals("unex # hosts", 18, cache.hostCount());
406 - assertEquals("unex # hosts", 26, cache.linkCount()); 406 + assertEquals("unex # device-links", 8, cache.deviceLinkCount());
407 + assertEquals("unex # edge-links", 18, cache.edgeLinkCount());
408 + assertEquals("unex # synth-links", 0, cache.synthLinkCount());
407 } 409 }
408 } 410 }
......