blob: 58e1b13dff14792c4886e34ab823336c6cb19f88 [file] [log] [blame]
Simon Hunt5f6dbf82016-03-30 08:53:33 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
Simon Hunt5f6dbf82016-03-30 08:53:33 -07003 *
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
17package org.onosproject.ui.model.topo;
18
Simon Hunt338a3b42016-04-14 09:43:52 -070019import org.onosproject.cluster.NodeId;
Simon Huntc0f20c12016-05-09 09:30:20 -070020import org.onosproject.net.DeviceId;
21import org.onosproject.net.HostId;
Simon Huntc13082f2016-08-03 21:20:23 -070022import org.onosproject.net.PortNumber;
Simon Huntc0f20c12016-05-09 09:30:20 -070023import org.onosproject.net.region.RegionId;
Thomas Vachuskab877a6f2017-04-14 11:43:30 -070024import org.onosproject.ui.model.ServiceBundle;
Simon Hunt23fb1352016-04-11 12:15:19 -070025import org.slf4j.Logger;
26import org.slf4j.LoggerFactory;
27
Simon Huntd5b96732016-07-08 13:22:27 -070028import java.util.ArrayList;
29import java.util.Collections;
30import java.util.Comparator;
Simon Huntc0f20c12016-05-09 09:30:20 -070031import java.util.HashMap;
32import java.util.HashSet;
Simon Huntd5b96732016-07-08 13:22:27 -070033import java.util.List;
Simon Huntc0f20c12016-05-09 09:30:20 -070034import java.util.Map;
Simon Huntc13082f2016-08-03 21:20:23 -070035import java.util.Objects;
Simon Hunt23fb1352016-04-11 12:15:19 -070036import java.util.Set;
Simon Huntc13082f2016-08-03 21:20:23 -070037import java.util.stream.Collectors;
Simon Huntc0f20c12016-05-09 09:30:20 -070038
39import static com.google.common.base.MoreObjects.toStringHelper;
Simon Huntc13082f2016-08-03 21:20:23 -070040import static org.onosproject.ui.model.topo.UiLinkId.uiLinkId;
Simon Hunt23fb1352016-04-11 12:15:19 -070041
Simon Hunt5f6dbf82016-03-30 08:53:33 -070042/**
43 * Represents the overall network topology.
44 */
Simon Huntcda9c032016-04-11 10:32:54 -070045public class UiTopology extends UiElement {
Simon Hunt23fb1352016-04-11 12:15:19 -070046
Simon Hunt58a0dd02016-05-17 11:54:23 -070047 private static final String INDENT_1 = " ";
48 private static final String INDENT_2 = " ";
49 private static final String EOL = String.format("%n");
50
Simon Huntc0f20c12016-05-09 09:30:20 -070051 private static final String E_UNMAPPED =
52 "Attempting to retrieve unmapped {}: {}";
53
Simon Hunt642bc452016-05-04 19:34:45 -070054 private static final String DEFAULT_TOPOLOGY_ID = "TOPOLOGY-0";
55
Simon Hunt23fb1352016-04-11 12:15:19 -070056 private static final Logger log = LoggerFactory.getLogger(UiTopology.class);
57
Simon Huntd5b96732016-07-08 13:22:27 -070058 private static final Comparator<UiClusterMember> CLUSTER_MEMBER_COMPARATOR =
59 (o1, o2) -> o1.idAsString().compareTo(o2.idAsString());
60
Simon Huntc0f20c12016-05-09 09:30:20 -070061
62 // top level mappings of topology elements by ID
63 private final Map<NodeId, UiClusterMember> cnodeLookup = new HashMap<>();
64 private final Map<RegionId, UiRegion> regionLookup = new HashMap<>();
65 private final Map<DeviceId, UiDevice> deviceLookup = new HashMap<>();
66 private final Map<HostId, UiHost> hostLookup = new HashMap<>();
Simon Huntc13082f2016-08-03 21:20:23 -070067 private final Map<UiLinkId, UiDeviceLink> devLinkLookup = new HashMap<>();
68 private final Map<UiLinkId, UiEdgeLink> edgeLinkLookup = new HashMap<>();
69
70 // a cache of the computed synthetic links
71 private final List<UiSynthLink> synthLinks = new ArrayList<>();
Simon Huntc0f20c12016-05-09 09:30:20 -070072
Simon Huntb1ce2602016-07-23 14:04:31 -070073 // a container for devices, hosts, etc. belonging to no region
74 private final UiRegion nullRegion = new UiRegion(this, null);
75
Thomas Vachuskab877a6f2017-04-14 11:43:30 -070076 final ServiceBundle services;
77
78 /**
79 * Creates a new UI topology backed by the specified service bundle.
80 *
81 * @param services service bundle
82 */
83 public UiTopology(ServiceBundle services) {
84 this.services = services;
85 }
Simon Hunt23fb1352016-04-11 12:15:19 -070086
Simon Hunt338a3b42016-04-14 09:43:52 -070087 @Override
88 public String toString() {
Simon Huntc0f20c12016-05-09 09:30:20 -070089 return toStringHelper(this)
90 .add("#cnodes", clusterMemberCount())
91 .add("#regions", regionCount())
92 .add("#devices", deviceLookup.size())
93 .add("#hosts", hostLookup.size())
Simon Huntc13082f2016-08-03 21:20:23 -070094 .add("#dev-links", devLinkLookup.size())
95 .add("#edge-links", edgeLinkLookup.size())
96 .add("#synth-links", synthLinks.size())
Simon Huntc0f20c12016-05-09 09:30:20 -070097 .toString();
98 }
99
100 @Override
101 public String idAsString() {
102 return DEFAULT_TOPOLOGY_ID;
Simon Hunt338a3b42016-04-14 09:43:52 -0700103 }
104
Simon Hunt23fb1352016-04-11 12:15:19 -0700105 /**
106 * Clears the topology state; that is, drops all regions, devices, hosts,
107 * links, and cluster members.
108 */
109 public void clear() {
110 log.debug("clearing topology model");
Simon Huntc0f20c12016-05-09 09:30:20 -0700111 cnodeLookup.clear();
112 regionLookup.clear();
113 deviceLookup.clear();
114 hostLookup.clear();
Simon Huntc13082f2016-08-03 21:20:23 -0700115 devLinkLookup.clear();
116 edgeLinkLookup.clear();
117
118 synthLinks.clear();
Simon Huntb1ce2602016-07-23 14:04:31 -0700119
120 nullRegion.destroy();
Simon Hunt23fb1352016-04-11 12:15:19 -0700121 }
Simon Hunt338a3b42016-04-14 09:43:52 -0700122
Simon Huntd5b96732016-07-08 13:22:27 -0700123
124 /**
125 * Returns all the cluster members, sorted by their ID.
126 *
127 * @return all cluster members
128 */
129 public List<UiClusterMember> allClusterMembers() {
130 List<UiClusterMember> members = new ArrayList<>(cnodeLookup.values());
131 Collections.sort(members, CLUSTER_MEMBER_COMPARATOR);
132 return members;
133 }
134
Simon Hunt338a3b42016-04-14 09:43:52 -0700135 /**
136 * Returns the cluster member with the given identifier, or null if no
Simon Huntc0f20c12016-05-09 09:30:20 -0700137 * such member exists.
Simon Hunt338a3b42016-04-14 09:43:52 -0700138 *
139 * @param id cluster node identifier
Simon Huntc0f20c12016-05-09 09:30:20 -0700140 * @return corresponding UI cluster member
Simon Hunt338a3b42016-04-14 09:43:52 -0700141 */
142 public UiClusterMember findClusterMember(NodeId id) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700143 return cnodeLookup.get(id);
Simon Hunt338a3b42016-04-14 09:43:52 -0700144 }
145
146 /**
147 * Adds the given cluster member to the topology model.
148 *
149 * @param member cluster member to add
150 */
151 public void add(UiClusterMember member) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700152 cnodeLookup.put(member.id(), member);
Simon Hunt338a3b42016-04-14 09:43:52 -0700153 }
154
155 /**
Simon Hunt642bc452016-05-04 19:34:45 -0700156 * Removes the given cluster member from the topology model.
157 *
158 * @param member cluster member to remove
159 */
160 public void remove(UiClusterMember member) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700161 UiClusterMember m = cnodeLookup.remove(member.id());
162 if (m != null) {
163 m.destroy();
164 }
Simon Hunt642bc452016-05-04 19:34:45 -0700165 }
166
167 /**
Simon Hunt338a3b42016-04-14 09:43:52 -0700168 * Returns the number of members in the cluster.
169 *
170 * @return number of cluster members
171 */
172 public int clusterMemberCount() {
Simon Huntc0f20c12016-05-09 09:30:20 -0700173 return cnodeLookup.size();
174 }
175
Simon Hunt10973dd2016-08-01 15:50:35 -0700176
177 /**
Simon Hunt4f4ffc32016-08-03 18:30:47 -0700178 * Returns all regions in the model (except the
179 * {@link #nullRegion() null region}).
Simon Hunt10973dd2016-08-01 15:50:35 -0700180 *
181 * @return all regions
182 */
183 public Set<UiRegion> allRegions() {
184 return new HashSet<>(regionLookup.values());
185 }
186
Simon Huntc0f20c12016-05-09 09:30:20 -0700187 /**
Simon Huntb1ce2602016-07-23 14:04:31 -0700188 * Returns a reference to the null-region. That is, the container for
189 * devices, hosts, and links that belong to no region.
190 *
191 * @return the null-region
192 */
193 public UiRegion nullRegion() {
194 return nullRegion;
195 }
196
197 /**
Simon Huntc0f20c12016-05-09 09:30:20 -0700198 * Returns the region with the specified identifier, or null if
199 * no such region exists.
200 *
201 * @param id region identifier
202 * @return corresponding UI region
203 */
204 public UiRegion findRegion(RegionId id) {
Simon Hunt4f4ffc32016-08-03 18:30:47 -0700205 return UiRegion.NULL_ID.equals(id) ? nullRegion() : regionLookup.get(id);
Simon Hunt338a3b42016-04-14 09:43:52 -0700206 }
207
208 /**
Simon Huntc0f20c12016-05-09 09:30:20 -0700209 * Adds the given region to the topology model.
210 *
211 * @param uiRegion region to add
212 */
213 public void add(UiRegion uiRegion) {
214 regionLookup.put(uiRegion.id(), uiRegion);
Simon Hunt642bc452016-05-04 19:34:45 -0700215 }
Simon Huntc0f20c12016-05-09 09:30:20 -0700216
217 /**
218 * Removes the given region from the topology model.
219 *
220 * @param uiRegion region to remove
221 */
222 public void remove(UiRegion uiRegion) {
Simon Hunt58a0dd02016-05-17 11:54:23 -0700223 UiRegion r = regionLookup.remove(uiRegion.id());
224 if (r != null) {
225 r.destroy();
226 }
227 }
228
229 /**
230 * Returns the number of regions configured in the topology.
231 *
232 * @return number of regions
233 */
234 public int regionCount() {
235 return regionLookup.size();
Simon Huntc0f20c12016-05-09 09:30:20 -0700236 }
237
238 /**
Simon Huntb1ce2602016-07-23 14:04:31 -0700239 * Returns all devices in the model.
240 *
241 * @return all devices
242 */
243 public Set<UiDevice> allDevices() {
244 return new HashSet<>(deviceLookup.values());
245 }
246
247 /**
Simon Huntc0f20c12016-05-09 09:30:20 -0700248 * Returns the device with the specified identifier, or null if
249 * no such device exists.
250 *
251 * @param id device identifier
252 * @return corresponding UI device
253 */
254 public UiDevice findDevice(DeviceId id) {
255 return deviceLookup.get(id);
256 }
257
258 /**
259 * Adds the given device to the topology model.
260 *
261 * @param uiDevice device to add
262 */
263 public void add(UiDevice uiDevice) {
264 deviceLookup.put(uiDevice.id(), uiDevice);
265 }
266
267 /**
268 * Removes the given device from the topology model.
269 *
270 * @param uiDevice device to remove
271 */
272 public void remove(UiDevice uiDevice) {
273 UiDevice d = deviceLookup.remove(uiDevice.id());
274 if (d != null) {
275 d.destroy();
276 }
277 }
278
279 /**
Simon Hunt58a0dd02016-05-17 11:54:23 -0700280 * Returns the number of devices configured in the topology.
281 *
282 * @return number of devices
283 */
284 public int deviceCount() {
285 return deviceLookup.size();
286 }
287
Simon Huntc13082f2016-08-03 21:20:23 -0700288
Simon Hunt58a0dd02016-05-17 11:54:23 -0700289 /**
Simon Huntc13082f2016-08-03 21:20:23 -0700290 * Returns all device links in the model.
Simon Hunt4854f3d2016-08-02 18:13:15 -0700291 *
Simon Huntc13082f2016-08-03 21:20:23 -0700292 * @return all device links
Simon Hunt4854f3d2016-08-02 18:13:15 -0700293 */
Simon Huntc13082f2016-08-03 21:20:23 -0700294 public Set<UiDeviceLink> allDeviceLinks() {
295 return new HashSet<>(devLinkLookup.values());
Simon Hunt4854f3d2016-08-02 18:13:15 -0700296 }
297
298 /**
Simon Huntc13082f2016-08-03 21:20:23 -0700299 * Returns the device link with the specified identifier, or null if no
300 * such link exists.
Simon Huntc0f20c12016-05-09 09:30:20 -0700301 *
302 * @param id the canonicalized link identifier
Simon Huntc13082f2016-08-03 21:20:23 -0700303 * @return corresponding UI device link
Simon Huntc0f20c12016-05-09 09:30:20 -0700304 */
Simon Huntc13082f2016-08-03 21:20:23 -0700305 public UiDeviceLink findDeviceLink(UiLinkId id) {
306 return devLinkLookup.get(id);
Simon Huntc0f20c12016-05-09 09:30:20 -0700307 }
308
309 /**
Simon Huntc13082f2016-08-03 21:20:23 -0700310 * Returns the edge link with the specified identifier, or null if no
311 * such link exists.
Simon Huntc0f20c12016-05-09 09:30:20 -0700312 *
Simon Huntc13082f2016-08-03 21:20:23 -0700313 * @param id the canonicalized link identifier
314 * @return corresponding UI edge link
Simon Huntc0f20c12016-05-09 09:30:20 -0700315 */
Simon Huntc13082f2016-08-03 21:20:23 -0700316 public UiEdgeLink findEdgeLink(UiLinkId id) {
317 return edgeLinkLookup.get(id);
Simon Huntc0f20c12016-05-09 09:30:20 -0700318 }
319
320 /**
Simon Huntc13082f2016-08-03 21:20:23 -0700321 * Adds the given UI device link to the topology model.
Simon Huntc0f20c12016-05-09 09:30:20 -0700322 *
Simon Huntc13082f2016-08-03 21:20:23 -0700323 * @param uiDeviceLink link to add
Simon Huntc0f20c12016-05-09 09:30:20 -0700324 */
Simon Huntc13082f2016-08-03 21:20:23 -0700325 public void add(UiDeviceLink uiDeviceLink) {
326 devLinkLookup.put(uiDeviceLink.id(), uiDeviceLink);
327 }
328
329 /**
330 * Adds the given UI edge link to the topology model.
331 *
332 * @param uiEdgeLink link to add
333 */
334 public void add(UiEdgeLink uiEdgeLink) {
335 edgeLinkLookup.put(uiEdgeLink.id(), uiEdgeLink);
336 }
337
338 /**
339 * Removes the given UI device link from the model.
340 *
341 * @param uiDeviceLink link to remove
342 */
343 public void remove(UiDeviceLink uiDeviceLink) {
344 UiDeviceLink link = devLinkLookup.remove(uiDeviceLink.id());
Simon Huntc0f20c12016-05-09 09:30:20 -0700345 if (link != null) {
346 link.destroy();
347 }
348 }
349
350 /**
Simon Huntc13082f2016-08-03 21:20:23 -0700351 * Removes the given UI edge link from the model.
Simon Hunt58a0dd02016-05-17 11:54:23 -0700352 *
Simon Huntc13082f2016-08-03 21:20:23 -0700353 * @param uiEdgeLink link to remove
Simon Hunt58a0dd02016-05-17 11:54:23 -0700354 */
Simon Huntc13082f2016-08-03 21:20:23 -0700355 public void remove(UiEdgeLink uiEdgeLink) {
356 UiEdgeLink link = edgeLinkLookup.remove(uiEdgeLink.id());
357 if (link != null) {
358 link.destroy();
359 }
360 }
361
362 /**
363 * Returns the number of device links configured in the topology.
364 *
365 * @return number of device links
366 */
367 public int deviceLinkCount() {
368 return devLinkLookup.size();
369 }
370
371 /**
372 * Returns the number of edge links configured in the topology.
373 *
374 * @return number of edge links
375 */
376 public int edgeLinkCount() {
377 return edgeLinkLookup.size();
Simon Hunt58a0dd02016-05-17 11:54:23 -0700378 }
379
380 /**
Simon Hunt4854f3d2016-08-02 18:13:15 -0700381 * Returns all hosts in the model.
382 *
383 * @return all hosts
384 */
385 public Set<UiHost> allHosts() {
386 return new HashSet<>(hostLookup.values());
387 }
388
389 /**
Simon Huntc0f20c12016-05-09 09:30:20 -0700390 * Returns the host with the specified identifier, or null if no such
391 * host exists.
392 *
393 * @param id host identifier
394 * @return corresponding UI host
395 */
396 public UiHost findHost(HostId id) {
397 return hostLookup.get(id);
398 }
399
400 /**
401 * Adds the given host to the topology model.
402 *
403 * @param uiHost host to add
404 */
405 public void add(UiHost uiHost) {
406 hostLookup.put(uiHost.id(), uiHost);
407 }
408
409 /**
410 * Removes the given host from the topology model.
411 *
412 * @param uiHost host to remove
413 */
414 public void remove(UiHost uiHost) {
415 UiHost h = hostLookup.remove(uiHost.id());
416 if (h != null) {
417 h.destroy();
418 }
419 }
420
Simon Hunt58a0dd02016-05-17 11:54:23 -0700421 /**
422 * Returns the number of hosts configured in the topology.
423 *
424 * @return number of hosts
425 */
426 public int hostCount() {
427 return hostLookup.size();
428 }
429
430
Simon Huntc0f20c12016-05-09 09:30:20 -0700431 // ==
432 // package private methods for supporting linkage amongst topology entities
433 // ==
434
435 /**
436 * Returns the set of UI devices with the given identifiers.
437 *
438 * @param deviceIds device identifiers
439 * @return set of matching UI device instances
440 */
441 Set<UiDevice> deviceSet(Set<DeviceId> deviceIds) {
442 Set<UiDevice> uiDevices = new HashSet<>();
443 for (DeviceId id : deviceIds) {
444 UiDevice d = deviceLookup.get(id);
445 if (d != null) {
446 uiDevices.add(d);
447 } else {
448 log.warn(E_UNMAPPED, "device", id);
449 }
450 }
451 return uiDevices;
452 }
453
454 /**
455 * Returns the set of UI hosts with the given identifiers.
456 *
457 * @param hostIds host identifiers
458 * @return set of matching UI host instances
459 */
460 Set<UiHost> hostSet(Set<HostId> hostIds) {
461 Set<UiHost> uiHosts = new HashSet<>();
462 for (HostId id : hostIds) {
463 UiHost h = hostLookup.get(id);
464 if (h != null) {
465 uiHosts.add(h);
466 } else {
467 log.warn(E_UNMAPPED, "host", id);
468 }
469 }
470 return uiHosts;
471 }
472
473 /**
Simon Huntc13082f2016-08-03 21:20:23 -0700474 * Returns the set of UI device links with the given identifiers.
Simon Huntc0f20c12016-05-09 09:30:20 -0700475 *
476 * @param uiLinkIds link identifiers
Simon Huntc13082f2016-08-03 21:20:23 -0700477 * @return set of matching UI device link instances
Simon Huntc0f20c12016-05-09 09:30:20 -0700478 */
Simon Huntc13082f2016-08-03 21:20:23 -0700479 Set<UiDeviceLink> linkSet(Set<UiLinkId> uiLinkIds) {
480 Set<UiDeviceLink> result = new HashSet<>();
Simon Huntc0f20c12016-05-09 09:30:20 -0700481 for (UiLinkId id : uiLinkIds) {
Simon Huntc13082f2016-08-03 21:20:23 -0700482 UiDeviceLink link = devLinkLookup.get(id);
Simon Huntc0f20c12016-05-09 09:30:20 -0700483 if (link != null) {
Simon Huntc13082f2016-08-03 21:20:23 -0700484 result.add(link);
Simon Huntc0f20c12016-05-09 09:30:20 -0700485 } else {
Simon Huntc13082f2016-08-03 21:20:23 -0700486 log.warn(E_UNMAPPED, "device link", id);
Simon Huntc0f20c12016-05-09 09:30:20 -0700487 }
488 }
Simon Huntc13082f2016-08-03 21:20:23 -0700489 return result;
490 }
491
492 /**
493 * Uses the device-device links and data about the regions to compute the
494 * set of synthetic links that are required per region.
495 */
496 public void computeSynthLinks() {
497 List<UiSynthLink> slinks = new ArrayList<>();
498 allDeviceLinks().forEach((link) -> {
499 UiSynthLink synthetic = inferSyntheticLink(link);
500 slinks.add(synthetic);
501 log.debug("Synthetic link: {}", synthetic);
502 });
503
Simon Huntb7fd0802016-10-27 12:21:40 -0700504 slinks.addAll(wrapHostLinks(nullRegion()));
505 for (UiRegion r: allRegions()) {
506 slinks.addAll(wrapHostLinks(r));
507 }
Simon Huntf59d36b2016-10-04 19:05:53 -0700508
Simon Huntc13082f2016-08-03 21:20:23 -0700509 synthLinks.clear();
510 synthLinks.addAll(slinks);
Simon Huntb7fd0802016-10-27 12:21:40 -0700511 }
Simon Huntf59d36b2016-10-04 19:05:53 -0700512
Simon Huntb7fd0802016-10-27 12:21:40 -0700513 private Set<UiSynthLink> wrapHostLinks(UiRegion region) {
514 RegionId regionId = region.id();
515 return region.hosts().stream().map(h -> wrapHostLink(regionId, h))
516 .collect(Collectors.toSet());
517 }
Simon Huntf59d36b2016-10-04 19:05:53 -0700518
Simon Huntb7fd0802016-10-27 12:21:40 -0700519 private UiSynthLink wrapHostLink(RegionId regionId, UiHost host) {
520 UiEdgeLink elink = new UiEdgeLink(this, host.edgeLinkId());
521 return new UiSynthLink(regionId, elink);
Simon Huntc13082f2016-08-03 21:20:23 -0700522 }
523
524 private UiSynthLink inferSyntheticLink(UiDeviceLink link) {
525 /*
526 Look at the containment hierarchy of each end of the link. Find the
527 common ancestor region R. A synthetic link will be added to R, based
528 on the "next" node back down the branch...
529
530 S1 --- S2 * in the same region ...
531 : :
532 R R return S1 --- S2 (same link instance)
533
534
535 S1 --- S2 * in different regions (R1, R2) at same level
536 : :
537 R1 R2 return R1 --- R2
538 : :
539 R R
540
541 S1 --- S2 * in different regions at different levels
542 : :
543 R1 R2 return R1 --- R3
544 : :
545 R R3
546 :
547 R
548
549 S1 --- S2 * in different regions at different levels
550 : :
551 R R2 return S1 --- R2
552 :
553 R
554
555 */
556 DeviceId a = link.deviceA();
557 DeviceId b = link.deviceB();
558 List<RegionId> aBranch = ancestors(a);
559 List<RegionId> bBranch = ancestors(b);
560 if (aBranch == null || bBranch == null) {
561 return null;
562 }
563
564 return makeSynthLink(link, aBranch, bBranch);
565 }
566
567 // package private for unit testing
568 UiSynthLink makeSynthLink(UiDeviceLink orig,
569 List<RegionId> aBranch,
570 List<RegionId> bBranch) {
571
572 final int aSize = aBranch.size();
573 final int bSize = bBranch.size();
574 final int min = Math.min(aSize, bSize);
575
576 int index = 0;
577 RegionId commonRegion = aBranch.get(index);
578
579 while (true) {
580 int next = index + 1;
581 if (next == min) {
582 // no more pairs of regions left to test
583 break;
584 }
585 RegionId rA = aBranch.get(next);
586 RegionId rB = bBranch.get(next);
587 if (rA.equals(rB)) {
588 commonRegion = rA;
589 index++;
590 } else {
591 break;
592 }
593 }
594
595
596 int endPointIndex = index + 1;
597 UiLinkId linkId;
598 UiLink link;
599
600 if (endPointIndex < aSize) {
601 // the A endpoint is a subregion
602 RegionId aRegion = aBranch.get(endPointIndex);
603
604 if (endPointIndex < bSize) {
605 // the B endpoint is a subregion
606 RegionId bRegion = bBranch.get(endPointIndex);
607
608 linkId = uiLinkId(aRegion, bRegion);
609 link = new UiRegionLink(this, linkId);
610
611 } else {
612 // the B endpoint is the device
613 DeviceId dB = orig.deviceB();
614 PortNumber pB = orig.portB();
615
616 linkId = uiLinkId(aRegion, dB, pB);
617 link = new UiRegionDeviceLink(this, linkId);
618 }
619
620 } else {
621 // the A endpoint is the device
622 DeviceId dA = orig.deviceA();
623 PortNumber pA = orig.portA();
624
625 if (endPointIndex < bSize) {
626 // the B endpoint is a subregion
627 RegionId bRegion = bBranch.get(endPointIndex);
628
629 linkId = uiLinkId(bRegion, dA, pA);
630 link = new UiRegionDeviceLink(this, linkId);
631
632 } else {
633 // the B endpoint is the device
634 // (so, we can just use the original device-device link...)
635
636 link = orig;
637 }
638 }
639 return new UiSynthLink(commonRegion, link);
640 }
641
642 private List<RegionId> ancestors(DeviceId id) {
643 // return the ancestor chain from this device to root region
644 UiDevice dev = findDevice(id);
645 if (dev == null) {
646 log.warn("Unable to find cached device with ID %s", id);
647 return null;
648 }
649
650 UiRegion r = dev.uiRegion();
651 List<RegionId> result = new ArrayList<>();
652 while (r != null && !r.isRoot()) {
653 result.add(0, r.id());
654 r = r.parentRegion();
655 }
656 // finally add root region, since this is the grand-daddy of them all
657 result.add(0, UiRegion.NULL_ID);
658 return result;
659 }
660
661
662 /**
663 * Returns the synthetic links associated with the specified region.
664 *
665 * @param regionId the region ID
666 * @return synthetic links for this region
667 */
668 public List<UiSynthLink> findSynthLinks(RegionId regionId) {
669 return synthLinks.stream()
670 .filter(s -> Objects.equals(regionId, s.regionId()))
671 .collect(Collectors.toList());
672 }
673
674
675 /**
676 * Returns the number of synthetic links in the topology.
677 *
678 * @return the synthetic link count
679 */
680 public int synthLinkCount() {
681 return synthLinks.size();
Simon Huntc0f20c12016-05-09 09:30:20 -0700682 }
683
Simon Hunt58a0dd02016-05-17 11:54:23 -0700684 /**
685 * Returns a detailed (multi-line) string showing the contents of the
686 * topology.
687 *
688 * @return detailed string
689 */
690 public String dumpString() {
691 StringBuilder sb = new StringBuilder("Topology:").append(EOL);
692
693 sb.append(INDENT_1).append("Cluster Members").append(EOL);
694 for (UiClusterMember m : cnodeLookup.values()) {
695 sb.append(INDENT_2).append(m).append(EOL);
696 }
697
698 sb.append(INDENT_1).append("Regions").append(EOL);
699 for (UiRegion r : regionLookup.values()) {
700 sb.append(INDENT_2).append(r).append(EOL);
701 }
702
703 sb.append(INDENT_1).append("Devices").append(EOL);
704 for (UiDevice d : deviceLookup.values()) {
705 sb.append(INDENT_2).append(d).append(EOL);
706 }
707
708 sb.append(INDENT_1).append("Hosts").append(EOL);
709 for (UiHost h : hostLookup.values()) {
710 sb.append(INDENT_2).append(h).append(EOL);
711 }
712
Simon Huntc13082f2016-08-03 21:20:23 -0700713 sb.append(INDENT_1).append("Device Links").append(EOL);
714 for (UiLink link : devLinkLookup.values()) {
715 sb.append(INDENT_2).append(link).append(EOL);
716 }
717
718 sb.append(INDENT_1).append("Edge Links").append(EOL);
719 for (UiLink link : edgeLinkLookup.values()) {
720 sb.append(INDENT_2).append(link).append(EOL);
721 }
722
723 sb.append(INDENT_1).append("Synth Links").append(EOL);
724 for (UiSynthLink link : synthLinks) {
Simon Hunt58a0dd02016-05-17 11:54:23 -0700725 sb.append(INDENT_2).append(link).append(EOL);
726 }
727 sb.append("------").append(EOL);
728
729 return sb.toString();
730 }
Simon Hunt5f6dbf82016-03-30 08:53:33 -0700731}