blob: 8c8740b52868b6d1e55144b499f26ba1851f8b8e [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());
Thomas Vachuska8c0b18a2017-04-14 16:27:33 -0700274 // TODO: Update the containing region
Simon Huntc0f20c12016-05-09 09:30:20 -0700275 if (d != null) {
276 d.destroy();
277 }
278 }
279
280 /**
Simon Hunt58a0dd02016-05-17 11:54:23 -0700281 * Returns the number of devices configured in the topology.
282 *
283 * @return number of devices
284 */
285 public int deviceCount() {
286 return deviceLookup.size();
287 }
288
Simon Huntc13082f2016-08-03 21:20:23 -0700289
Simon Hunt58a0dd02016-05-17 11:54:23 -0700290 /**
Simon Huntc13082f2016-08-03 21:20:23 -0700291 * Returns all device links in the model.
Simon Hunt4854f3d2016-08-02 18:13:15 -0700292 *
Simon Huntc13082f2016-08-03 21:20:23 -0700293 * @return all device links
Simon Hunt4854f3d2016-08-02 18:13:15 -0700294 */
Simon Huntc13082f2016-08-03 21:20:23 -0700295 public Set<UiDeviceLink> allDeviceLinks() {
296 return new HashSet<>(devLinkLookup.values());
Simon Hunt4854f3d2016-08-02 18:13:15 -0700297 }
298
299 /**
Simon Huntc13082f2016-08-03 21:20:23 -0700300 * Returns the device link with the specified identifier, or null if no
301 * such link exists.
Simon Huntc0f20c12016-05-09 09:30:20 -0700302 *
303 * @param id the canonicalized link identifier
Simon Huntc13082f2016-08-03 21:20:23 -0700304 * @return corresponding UI device link
Simon Huntc0f20c12016-05-09 09:30:20 -0700305 */
Simon Huntc13082f2016-08-03 21:20:23 -0700306 public UiDeviceLink findDeviceLink(UiLinkId id) {
307 return devLinkLookup.get(id);
Simon Huntc0f20c12016-05-09 09:30:20 -0700308 }
309
310 /**
Simon Huntc13082f2016-08-03 21:20:23 -0700311 * Returns the edge link with the specified identifier, or null if no
312 * such link exists.
Simon Huntc0f20c12016-05-09 09:30:20 -0700313 *
Simon Huntc13082f2016-08-03 21:20:23 -0700314 * @param id the canonicalized link identifier
315 * @return corresponding UI edge link
Simon Huntc0f20c12016-05-09 09:30:20 -0700316 */
Simon Huntc13082f2016-08-03 21:20:23 -0700317 public UiEdgeLink findEdgeLink(UiLinkId id) {
318 return edgeLinkLookup.get(id);
Simon Huntc0f20c12016-05-09 09:30:20 -0700319 }
320
321 /**
Simon Huntc13082f2016-08-03 21:20:23 -0700322 * Adds the given UI device link to the topology model.
Simon Huntc0f20c12016-05-09 09:30:20 -0700323 *
Simon Huntc13082f2016-08-03 21:20:23 -0700324 * @param uiDeviceLink link to add
Simon Huntc0f20c12016-05-09 09:30:20 -0700325 */
Simon Huntc13082f2016-08-03 21:20:23 -0700326 public void add(UiDeviceLink uiDeviceLink) {
327 devLinkLookup.put(uiDeviceLink.id(), uiDeviceLink);
328 }
329
330 /**
331 * Adds the given UI edge link to the topology model.
332 *
333 * @param uiEdgeLink link to add
334 */
335 public void add(UiEdgeLink uiEdgeLink) {
336 edgeLinkLookup.put(uiEdgeLink.id(), uiEdgeLink);
337 }
338
339 /**
340 * Removes the given UI device link from the model.
341 *
342 * @param uiDeviceLink link to remove
343 */
344 public void remove(UiDeviceLink uiDeviceLink) {
345 UiDeviceLink link = devLinkLookup.remove(uiDeviceLink.id());
Simon Huntc0f20c12016-05-09 09:30:20 -0700346 if (link != null) {
347 link.destroy();
348 }
349 }
350
351 /**
Simon Huntc13082f2016-08-03 21:20:23 -0700352 * Removes the given UI edge link from the model.
Simon Hunt58a0dd02016-05-17 11:54:23 -0700353 *
Simon Huntc13082f2016-08-03 21:20:23 -0700354 * @param uiEdgeLink link to remove
Simon Hunt58a0dd02016-05-17 11:54:23 -0700355 */
Simon Huntc13082f2016-08-03 21:20:23 -0700356 public void remove(UiEdgeLink uiEdgeLink) {
357 UiEdgeLink link = edgeLinkLookup.remove(uiEdgeLink.id());
358 if (link != null) {
359 link.destroy();
360 }
361 }
362
363 /**
364 * Returns the number of device links configured in the topology.
365 *
366 * @return number of device links
367 */
368 public int deviceLinkCount() {
369 return devLinkLookup.size();
370 }
371
372 /**
373 * Returns the number of edge links configured in the topology.
374 *
375 * @return number of edge links
376 */
377 public int edgeLinkCount() {
378 return edgeLinkLookup.size();
Simon Hunt58a0dd02016-05-17 11:54:23 -0700379 }
380
381 /**
Simon Hunt4854f3d2016-08-02 18:13:15 -0700382 * Returns all hosts in the model.
383 *
384 * @return all hosts
385 */
386 public Set<UiHost> allHosts() {
387 return new HashSet<>(hostLookup.values());
388 }
389
390 /**
Simon Huntc0f20c12016-05-09 09:30:20 -0700391 * Returns the host with the specified identifier, or null if no such
392 * host exists.
393 *
394 * @param id host identifier
395 * @return corresponding UI host
396 */
397 public UiHost findHost(HostId id) {
398 return hostLookup.get(id);
399 }
400
401 /**
402 * Adds the given host to the topology model.
403 *
404 * @param uiHost host to add
405 */
406 public void add(UiHost uiHost) {
407 hostLookup.put(uiHost.id(), uiHost);
408 }
409
410 /**
411 * Removes the given host from the topology model.
412 *
413 * @param uiHost host to remove
414 */
415 public void remove(UiHost uiHost) {
416 UiHost h = hostLookup.remove(uiHost.id());
417 if (h != null) {
418 h.destroy();
419 }
420 }
421
Simon Hunt58a0dd02016-05-17 11:54:23 -0700422 /**
423 * Returns the number of hosts configured in the topology.
424 *
425 * @return number of hosts
426 */
427 public int hostCount() {
428 return hostLookup.size();
429 }
430
431
Simon Huntc0f20c12016-05-09 09:30:20 -0700432 // ==
433 // package private methods for supporting linkage amongst topology entities
434 // ==
435
436 /**
437 * Returns the set of UI devices with the given identifiers.
438 *
439 * @param deviceIds device identifiers
440 * @return set of matching UI device instances
441 */
442 Set<UiDevice> deviceSet(Set<DeviceId> deviceIds) {
443 Set<UiDevice> uiDevices = new HashSet<>();
444 for (DeviceId id : deviceIds) {
445 UiDevice d = deviceLookup.get(id);
446 if (d != null) {
447 uiDevices.add(d);
448 } else {
449 log.warn(E_UNMAPPED, "device", id);
450 }
451 }
452 return uiDevices;
453 }
454
455 /**
456 * Returns the set of UI hosts with the given identifiers.
457 *
458 * @param hostIds host identifiers
459 * @return set of matching UI host instances
460 */
461 Set<UiHost> hostSet(Set<HostId> hostIds) {
462 Set<UiHost> uiHosts = new HashSet<>();
463 for (HostId id : hostIds) {
464 UiHost h = hostLookup.get(id);
465 if (h != null) {
466 uiHosts.add(h);
467 } else {
468 log.warn(E_UNMAPPED, "host", id);
469 }
470 }
471 return uiHosts;
472 }
473
474 /**
Simon Huntc13082f2016-08-03 21:20:23 -0700475 * Returns the set of UI device links with the given identifiers.
Simon Huntc0f20c12016-05-09 09:30:20 -0700476 *
477 * @param uiLinkIds link identifiers
Simon Huntc13082f2016-08-03 21:20:23 -0700478 * @return set of matching UI device link instances
Simon Huntc0f20c12016-05-09 09:30:20 -0700479 */
Simon Huntc13082f2016-08-03 21:20:23 -0700480 Set<UiDeviceLink> linkSet(Set<UiLinkId> uiLinkIds) {
481 Set<UiDeviceLink> result = new HashSet<>();
Simon Huntc0f20c12016-05-09 09:30:20 -0700482 for (UiLinkId id : uiLinkIds) {
Simon Huntc13082f2016-08-03 21:20:23 -0700483 UiDeviceLink link = devLinkLookup.get(id);
Simon Huntc0f20c12016-05-09 09:30:20 -0700484 if (link != null) {
Simon Huntc13082f2016-08-03 21:20:23 -0700485 result.add(link);
Simon Huntc0f20c12016-05-09 09:30:20 -0700486 } else {
Simon Huntc13082f2016-08-03 21:20:23 -0700487 log.warn(E_UNMAPPED, "device link", id);
Simon Huntc0f20c12016-05-09 09:30:20 -0700488 }
489 }
Simon Huntc13082f2016-08-03 21:20:23 -0700490 return result;
491 }
492
493 /**
494 * Uses the device-device links and data about the regions to compute the
495 * set of synthetic links that are required per region.
496 */
497 public void computeSynthLinks() {
498 List<UiSynthLink> slinks = new ArrayList<>();
499 allDeviceLinks().forEach((link) -> {
500 UiSynthLink synthetic = inferSyntheticLink(link);
501 slinks.add(synthetic);
502 log.debug("Synthetic link: {}", synthetic);
503 });
504
Simon Huntb7fd0802016-10-27 12:21:40 -0700505 slinks.addAll(wrapHostLinks(nullRegion()));
506 for (UiRegion r: allRegions()) {
507 slinks.addAll(wrapHostLinks(r));
508 }
Simon Huntf59d36b2016-10-04 19:05:53 -0700509
Simon Huntc13082f2016-08-03 21:20:23 -0700510 synthLinks.clear();
511 synthLinks.addAll(slinks);
Simon Huntb7fd0802016-10-27 12:21:40 -0700512 }
Simon Huntf59d36b2016-10-04 19:05:53 -0700513
Simon Huntb7fd0802016-10-27 12:21:40 -0700514 private Set<UiSynthLink> wrapHostLinks(UiRegion region) {
515 RegionId regionId = region.id();
516 return region.hosts().stream().map(h -> wrapHostLink(regionId, h))
517 .collect(Collectors.toSet());
518 }
Simon Huntf59d36b2016-10-04 19:05:53 -0700519
Simon Huntb7fd0802016-10-27 12:21:40 -0700520 private UiSynthLink wrapHostLink(RegionId regionId, UiHost host) {
521 UiEdgeLink elink = new UiEdgeLink(this, host.edgeLinkId());
522 return new UiSynthLink(regionId, elink);
Simon Huntc13082f2016-08-03 21:20:23 -0700523 }
524
525 private UiSynthLink inferSyntheticLink(UiDeviceLink link) {
526 /*
527 Look at the containment hierarchy of each end of the link. Find the
528 common ancestor region R. A synthetic link will be added to R, based
529 on the "next" node back down the branch...
530
531 S1 --- S2 * in the same region ...
532 : :
533 R R return S1 --- S2 (same link instance)
534
535
536 S1 --- S2 * in different regions (R1, R2) at same level
537 : :
538 R1 R2 return R1 --- R2
539 : :
540 R R
541
542 S1 --- S2 * in different regions at different levels
543 : :
544 R1 R2 return R1 --- R3
545 : :
546 R R3
547 :
548 R
549
550 S1 --- S2 * in different regions at different levels
551 : :
552 R R2 return S1 --- R2
553 :
554 R
555
556 */
557 DeviceId a = link.deviceA();
558 DeviceId b = link.deviceB();
559 List<RegionId> aBranch = ancestors(a);
560 List<RegionId> bBranch = ancestors(b);
561 if (aBranch == null || bBranch == null) {
562 return null;
563 }
564
565 return makeSynthLink(link, aBranch, bBranch);
566 }
567
568 // package private for unit testing
569 UiSynthLink makeSynthLink(UiDeviceLink orig,
570 List<RegionId> aBranch,
571 List<RegionId> bBranch) {
572
573 final int aSize = aBranch.size();
574 final int bSize = bBranch.size();
575 final int min = Math.min(aSize, bSize);
576
577 int index = 0;
578 RegionId commonRegion = aBranch.get(index);
579
580 while (true) {
581 int next = index + 1;
582 if (next == min) {
583 // no more pairs of regions left to test
584 break;
585 }
586 RegionId rA = aBranch.get(next);
587 RegionId rB = bBranch.get(next);
588 if (rA.equals(rB)) {
589 commonRegion = rA;
590 index++;
591 } else {
592 break;
593 }
594 }
595
596
597 int endPointIndex = index + 1;
598 UiLinkId linkId;
599 UiLink link;
600
601 if (endPointIndex < aSize) {
602 // the A endpoint is a subregion
603 RegionId aRegion = aBranch.get(endPointIndex);
604
605 if (endPointIndex < bSize) {
606 // the B endpoint is a subregion
607 RegionId bRegion = bBranch.get(endPointIndex);
608
609 linkId = uiLinkId(aRegion, bRegion);
610 link = new UiRegionLink(this, linkId);
611
612 } else {
613 // the B endpoint is the device
614 DeviceId dB = orig.deviceB();
615 PortNumber pB = orig.portB();
616
617 linkId = uiLinkId(aRegion, dB, pB);
618 link = new UiRegionDeviceLink(this, linkId);
619 }
620
621 } else {
622 // the A endpoint is the device
623 DeviceId dA = orig.deviceA();
624 PortNumber pA = orig.portA();
625
626 if (endPointIndex < bSize) {
627 // the B endpoint is a subregion
628 RegionId bRegion = bBranch.get(endPointIndex);
629
630 linkId = uiLinkId(bRegion, dA, pA);
631 link = new UiRegionDeviceLink(this, linkId);
632
633 } else {
634 // the B endpoint is the device
635 // (so, we can just use the original device-device link...)
636
637 link = orig;
638 }
639 }
640 return new UiSynthLink(commonRegion, link);
641 }
642
643 private List<RegionId> ancestors(DeviceId id) {
644 // return the ancestor chain from this device to root region
645 UiDevice dev = findDevice(id);
646 if (dev == null) {
647 log.warn("Unable to find cached device with ID %s", id);
648 return null;
649 }
650
651 UiRegion r = dev.uiRegion();
652 List<RegionId> result = new ArrayList<>();
653 while (r != null && !r.isRoot()) {
654 result.add(0, r.id());
655 r = r.parentRegion();
656 }
657 // finally add root region, since this is the grand-daddy of them all
658 result.add(0, UiRegion.NULL_ID);
659 return result;
660 }
661
662
663 /**
664 * Returns the synthetic links associated with the specified region.
665 *
666 * @param regionId the region ID
667 * @return synthetic links for this region
668 */
669 public List<UiSynthLink> findSynthLinks(RegionId regionId) {
670 return synthLinks.stream()
671 .filter(s -> Objects.equals(regionId, s.regionId()))
672 .collect(Collectors.toList());
673 }
674
675
676 /**
677 * Returns the number of synthetic links in the topology.
678 *
679 * @return the synthetic link count
680 */
681 public int synthLinkCount() {
682 return synthLinks.size();
Simon Huntc0f20c12016-05-09 09:30:20 -0700683 }
684
Simon Hunt58a0dd02016-05-17 11:54:23 -0700685 /**
686 * Returns a detailed (multi-line) string showing the contents of the
687 * topology.
688 *
689 * @return detailed string
690 */
691 public String dumpString() {
692 StringBuilder sb = new StringBuilder("Topology:").append(EOL);
693
694 sb.append(INDENT_1).append("Cluster Members").append(EOL);
695 for (UiClusterMember m : cnodeLookup.values()) {
696 sb.append(INDENT_2).append(m).append(EOL);
697 }
698
699 sb.append(INDENT_1).append("Regions").append(EOL);
700 for (UiRegion r : regionLookup.values()) {
701 sb.append(INDENT_2).append(r).append(EOL);
702 }
703
704 sb.append(INDENT_1).append("Devices").append(EOL);
705 for (UiDevice d : deviceLookup.values()) {
706 sb.append(INDENT_2).append(d).append(EOL);
707 }
708
709 sb.append(INDENT_1).append("Hosts").append(EOL);
710 for (UiHost h : hostLookup.values()) {
711 sb.append(INDENT_2).append(h).append(EOL);
712 }
713
Simon Huntc13082f2016-08-03 21:20:23 -0700714 sb.append(INDENT_1).append("Device Links").append(EOL);
715 for (UiLink link : devLinkLookup.values()) {
716 sb.append(INDENT_2).append(link).append(EOL);
717 }
718
719 sb.append(INDENT_1).append("Edge Links").append(EOL);
720 for (UiLink link : edgeLinkLookup.values()) {
721 sb.append(INDENT_2).append(link).append(EOL);
722 }
723
724 sb.append(INDENT_1).append("Synth Links").append(EOL);
725 for (UiSynthLink link : synthLinks) {
Simon Hunt58a0dd02016-05-17 11:54:23 -0700726 sb.append(INDENT_2).append(link).append(EOL);
727 }
728 sb.append("------").append(EOL);
729
730 return sb.toString();
731 }
Simon Hunt5f6dbf82016-03-30 08:53:33 -0700732}