| /* |
| * Copyright 2016-present Open Networking Foundation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.onosproject.ui.impl.topo.model; |
| |
| import com.fasterxml.jackson.databind.node.ObjectNode; |
| import org.onosproject.cluster.ControllerNode; |
| import org.onosproject.cluster.NodeId; |
| import org.onosproject.cluster.RoleInfo; |
| import org.onosproject.event.EventDispatcher; |
| import org.onosproject.net.Device; |
| import org.onosproject.net.DeviceId; |
| import org.onosproject.net.EdgeLink; |
| import org.onosproject.net.Host; |
| import org.onosproject.net.HostId; |
| import org.onosproject.net.HostLocation; |
| import org.onosproject.net.Link; |
| import org.onosproject.net.region.Region; |
| import org.onosproject.net.region.RegionId; |
| import org.onosproject.ui.UiTopoLayoutService; |
| import org.onosproject.ui.impl.topo.Topo2Jsonifier; |
| import org.onosproject.ui.model.ServiceBundle; |
| import org.onosproject.ui.model.topo.UiClusterMember; |
| import org.onosproject.ui.model.topo.UiDevice; |
| import org.onosproject.ui.model.topo.UiDeviceLink; |
| import org.onosproject.ui.model.topo.UiEdgeLink; |
| import org.onosproject.ui.model.topo.UiElement; |
| import org.onosproject.ui.model.topo.UiHost; |
| import org.onosproject.ui.model.topo.UiLinkId; |
| import org.onosproject.ui.model.topo.UiModelEvent; |
| import org.onosproject.ui.model.topo.UiRegion; |
| import org.onosproject.ui.model.topo.UiSynthLink; |
| import org.onosproject.ui.model.topo.UiTopoLayout; |
| import org.onosproject.ui.model.topo.UiTopoLayoutId; |
| import org.onosproject.ui.model.topo.UiTopology; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import static org.onosproject.net.DefaultEdgeLink.createEdgeLink; |
| import static org.onosproject.ui.model.topo.UiModelEvent.Type.CLUSTER_MEMBER_ADDED_OR_UPDATED; |
| import static org.onosproject.ui.model.topo.UiModelEvent.Type.CLUSTER_MEMBER_REMOVED; |
| import static org.onosproject.ui.model.topo.UiModelEvent.Type.DEVICE_ADDED_OR_UPDATED; |
| import static org.onosproject.ui.model.topo.UiModelEvent.Type.DEVICE_REMOVED; |
| import static org.onosproject.ui.model.topo.UiModelEvent.Type.HOST_ADDED_OR_UPDATED; |
| import static org.onosproject.ui.model.topo.UiModelEvent.Type.HOST_MOVED; |
| import static org.onosproject.ui.model.topo.UiModelEvent.Type.HOST_REMOVED; |
| import static org.onosproject.ui.model.topo.UiModelEvent.Type.LINK_ADDED_OR_UPDATED; |
| import static org.onosproject.ui.model.topo.UiModelEvent.Type.LINK_REMOVED; |
| import static org.onosproject.ui.model.topo.UiModelEvent.Type.REGION_ADDED_OR_UPDATED; |
| import static org.onosproject.ui.model.topo.UiModelEvent.Type.REGION_REMOVED; |
| import static org.onosproject.ui.model.topo.UiLinkId.uiLinkId; |
| |
| /** |
| * UI Topology Model cache. |
| */ |
| class ModelCache { |
| |
| private static final String E_NO_ELEMENT = "Tried to remove non-member {}: {}"; |
| private static final String MEMO_ADDED = "added"; |
| private static final String MEMO_UPDATED = "updated"; |
| private static final String MEMO_REMOVED = "removed"; |
| private static final String MEMO_MOVED = "moved"; |
| |
| private static final Logger log = LoggerFactory.getLogger(ModelCache.class); |
| |
| // TODO: add NetworkConfigService to service bundle |
| // private final NetworkConfigService cfgService = |
| // DefaultServiceDirectory.getService(NetworkConfigService.class); |
| |
| private final ServiceBundle services; |
| private final EventDispatcher dispatcher; |
| private final UiTopology uiTopology; |
| |
| private Topo2Jsonifier t2json; |
| |
| ModelCache(ServiceBundle services, EventDispatcher eventDispatcher) { |
| this.services = services; |
| this.dispatcher = eventDispatcher; |
| uiTopology = new UiTopology(services); |
| } |
| |
| @Override |
| public String toString() { |
| return "ModelCache{" + uiTopology + "}"; |
| } |
| |
| private void postEvent(UiModelEvent.Type type, UiElement subject, String memo) { |
| ObjectNode data = t2json != null ? t2json.jsonUiElement(subject) : null; |
| dispatcher.post(new UiModelEvent(type, subject, data, memo)); |
| } |
| |
| void injectJsonifier(Topo2Jsonifier t2json) { |
| this.t2json = t2json; |
| } |
| |
| void clear() { |
| uiTopology.clear(); |
| } |
| |
| /** |
| * Create our internal model of the global topology. An assumption we are |
| * making is that the topology is empty to start. |
| */ |
| void load() { |
| loadClusterMembers(); |
| loadRegions(); |
| loadDevices(); |
| loadDeviceLinks(); |
| loadHosts(); |
| } |
| |
| |
| // === CLUSTER MEMBERS |
| |
| private UiClusterMember addNewClusterMember(ControllerNode n) { |
| UiClusterMember member = new UiClusterMember(uiTopology, n); |
| uiTopology.add(member); |
| return member; |
| } |
| |
| private void updateClusterMember(UiClusterMember member) { |
| // maybe something to update in the future? |
| } |
| |
| private void loadClusterMembers() { |
| for (ControllerNode n : services.cluster().getNodes()) { |
| UiClusterMember member = addNewClusterMember(n); |
| updateClusterMember(member); |
| } |
| } |
| |
| // invoked from UiSharedTopologyModel cluster event listener |
| void addOrUpdateClusterMember(ControllerNode cnode) { |
| NodeId id = cnode.id(); |
| String memo = MEMO_UPDATED; |
| UiClusterMember member = uiTopology.findClusterMember(id); |
| if (member == null) { |
| member = addNewClusterMember(cnode); |
| memo = MEMO_ADDED; |
| } |
| updateClusterMember(member); |
| |
| postEvent(CLUSTER_MEMBER_ADDED_OR_UPDATED, member, memo); |
| } |
| |
| // package private for unit test access |
| UiClusterMember accessClusterMember(NodeId id) { |
| return uiTopology.findClusterMember(id); |
| } |
| |
| // invoked from UiSharedTopologyModel cluster event listener |
| void removeClusterMember(ControllerNode cnode) { |
| NodeId id = cnode.id(); |
| UiClusterMember member = uiTopology.findClusterMember(id); |
| if (member != null) { |
| uiTopology.remove(member); |
| postEvent(CLUSTER_MEMBER_REMOVED, member, MEMO_REMOVED); |
| } else { |
| log.warn(E_NO_ELEMENT, "cluster node", id); |
| } |
| } |
| |
| List<UiClusterMember> getAllClusterMembers() { |
| return uiTopology.allClusterMembers(); |
| } |
| |
| |
| // === MASTERSHIP CHANGES |
| |
| // invoked from UiSharedTopologyModel mastership listener |
| void updateMasterships(DeviceId deviceId, RoleInfo roleInfo) { |
| // To think about:: do we need to store mastership info? |
| // or can we rely on looking it up live? |
| // TODO: store the updated mastership information |
| // TODO: post event |
| } |
| |
| // === THE NULL REGION |
| |
| UiRegion nullRegion() { |
| return uiTopology.nullRegion(); |
| } |
| |
| // === REGIONS |
| |
| private UiRegion addNewRegion(Region r) { |
| UiRegion region = new UiRegion(uiTopology, r); |
| uiTopology.add(region); |
| log.debug("Region {} added to topology", region); |
| return region; |
| } |
| |
| private void updateRegion(UiRegion region) { |
| RegionId rid = region.id(); |
| Set<DeviceId> deviceIds = services.region().getRegionDevices(rid); |
| Set<HostId> hostIds = services.region().getRegionHosts(rid); |
| |
| // Make sure device objects refer to their region |
| deviceIds.forEach(d -> { |
| UiDevice dev = uiTopology.findDevice(d); |
| if (dev != null) { |
| dev.setRegionId(rid); |
| } else { |
| // if we don't have the UiDevice in the topology, what can we do? |
| log.warn("Region device {}, but we don't have UiDevice in topology", d); |
| } |
| }); |
| |
| hostIds.forEach(d -> { |
| UiHost host = uiTopology.findHost(d); |
| if (host != null) { |
| host.setRegionId(rid); |
| } else { |
| // if we don't have the UiDevice in the topology, what can we do? |
| log.warn("Region host {}, but we don't have UiHost in topology", d); |
| } |
| }); |
| |
| // Make sure the region object refers to the devices |
| region.reconcileDevices(deviceIds); |
| region.reconcileHosts(hostIds); |
| |
| fixupContainmentHierarchy(region); |
| } |
| |
| private void fixupContainmentHierarchy(UiRegion region) { |
| UiTopoLayoutService ls = services.layout(); |
| RegionId regionId = region.id(); |
| |
| UiTopoLayout layout = ls.getLayout(regionId); |
| if (layout == null) { |
| // no layout backed by this region |
| log.warn("No layout backed by region {}", regionId); |
| return; |
| } |
| |
| UiTopoLayoutId layoutId = layout.id(); |
| |
| if (!layout.isRoot()) { |
| UiTopoLayoutId parentId = layout.parent(); |
| UiTopoLayout parentLayout = ls.getLayout(parentId); |
| RegionId parentRegionId = parentLayout.regionId(); |
| region.setParent(parentRegionId); |
| } |
| |
| Set<UiTopoLayout> kids = ls.getChildren(layoutId); |
| Set<RegionId> kidRegionIds = new HashSet<>(kids.size()); |
| kids.forEach(k -> kidRegionIds.add(k.regionId())); |
| region.setChildren(kidRegionIds); |
| } |
| |
| private void loadRegions() { |
| for (Region r : services.region().getRegions()) { |
| UiRegion region = addNewRegion(r); |
| updateRegion(region); |
| } |
| } |
| |
| // invoked from UiSharedTopologyModel region listener |
| void addOrUpdateRegion(Region region) { |
| RegionId id = region.id(); |
| String memo = MEMO_UPDATED; |
| UiRegion uiRegion = uiTopology.findRegion(id); |
| if (uiRegion == null) { |
| uiRegion = addNewRegion(region); |
| memo = MEMO_ADDED; |
| } |
| updateRegion(uiRegion); |
| |
| postEvent(REGION_ADDED_OR_UPDATED, uiRegion, memo); |
| } |
| |
| // package private for unit test access |
| UiRegion accessRegion(RegionId id) { |
| return id == null ? null : uiTopology.findRegion(id); |
| } |
| |
| // invoked from UiSharedTopologyModel region listener |
| void removeRegion(Region region) { |
| RegionId id = region.id(); |
| UiRegion uiRegion = uiTopology.findRegion(id); |
| if (uiRegion != null) { |
| uiTopology.remove(uiRegion); |
| postEvent(REGION_REMOVED, uiRegion, MEMO_REMOVED); |
| } else { |
| log.warn(E_NO_ELEMENT, "region", id); |
| } |
| } |
| |
| Set<UiRegion> getAllRegions() { |
| return uiTopology.allRegions(); |
| } |
| |
| |
| // === DEVICES |
| |
| private UiDevice addNewDevice(Device d) { |
| UiDevice device = new UiDevice(uiTopology, d); |
| updateDevice(device); |
| uiTopology.add(device); |
| log.debug("Device {} added to topology", device); |
| return device; |
| } |
| |
| // make sure the UiDevice is tagged with the region it belongs to |
| private void updateDevice(UiDevice device) { |
| Region r = services.region().getRegionForDevice(device.id()); |
| RegionId rid = r == null ? UiRegion.NULL_ID : r.id(); |
| device.setRegionId(rid); |
| } |
| |
| private void loadDevices() { |
| for (Device d : services.device().getDevices()) { |
| addNewDevice(d); |
| } |
| } |
| |
| // invoked from UiSharedTopologyModel device listener |
| void addOrUpdateDevice(Device device) { |
| DeviceId id = device.id(); |
| String memo = MEMO_UPDATED; |
| UiDevice uiDevice = uiTopology.findDevice(id); |
| if (uiDevice == null) { |
| uiDevice = addNewDevice(device); |
| memo = MEMO_ADDED; |
| } else { |
| updateDevice(uiDevice); |
| } |
| |
| postEvent(DEVICE_ADDED_OR_UPDATED, uiDevice, memo); |
| } |
| |
| // package private for unit test access |
| UiDevice accessDevice(DeviceId id) { |
| return uiTopology.findDevice(id); |
| } |
| |
| // invoked from UiSharedTopologyModel device listener |
| void removeDevice(Device device) { |
| DeviceId id = device.id(); |
| UiDevice uiDevice = uiTopology.findDevice(id); |
| if (uiDevice != null) { |
| uiTopology.remove(uiDevice); |
| postEvent(DEVICE_REMOVED, uiDevice, MEMO_REMOVED); |
| } else { |
| log.warn(E_NO_ELEMENT, "device", id); |
| } |
| } |
| |
| Set<UiDevice> getAllDevices() { |
| return uiTopology.allDevices(); |
| } |
| |
| |
| // === LINKS === |
| |
| private UiDeviceLink addNewDeviceLink(UiLinkId id) { |
| UiDeviceLink uiDeviceLink = new UiDeviceLink(uiTopology, id); |
| uiTopology.add(uiDeviceLink); |
| return uiDeviceLink; |
| } |
| |
| private UiEdgeLink addNewEdgeLink(UiLinkId id) { |
| UiEdgeLink uiEdgeLink = new UiEdgeLink(uiTopology, id); |
| uiTopology.add(uiEdgeLink); |
| return uiEdgeLink; |
| } |
| |
| private void updateDeviceLink(UiDeviceLink uiDeviceLink, Link link) { |
| uiDeviceLink.attachBackingLink(link); |
| } |
| |
| private void loadDeviceLinks() { |
| for (Link link : services.link().getLinks()) { |
| UiLinkId id = uiLinkId(link); |
| |
| UiDeviceLink uiDeviceLink = uiTopology.findDeviceLink(id); |
| if (uiDeviceLink == null) { |
| uiDeviceLink = addNewDeviceLink(id); |
| } |
| updateDeviceLink(uiDeviceLink, link); |
| } |
| } |
| |
| // invoked from UiSharedTopologyModel link listener |
| void addOrUpdateDeviceLink(Link link) { |
| UiLinkId id = uiLinkId(link); |
| String memo = MEMO_UPDATED; |
| UiDeviceLink uiDeviceLink = uiTopology.findDeviceLink(id); |
| if (uiDeviceLink == null) { |
| uiDeviceLink = addNewDeviceLink(id); |
| memo = MEMO_ADDED; |
| } |
| updateDeviceLink(uiDeviceLink, link); |
| |
| postEvent(LINK_ADDED_OR_UPDATED, uiDeviceLink, memo); |
| } |
| |
| // package private for unit test access |
| UiDeviceLink accessDeviceLink(UiLinkId id) { |
| return uiTopology.findDeviceLink(id); |
| } |
| |
| // invoked from UiSharedTopologyModel link listener |
| void removeDeviceLink(Link link) { |
| UiLinkId id = uiLinkId(link); |
| UiDeviceLink uiDeviceLink = uiTopology.findDeviceLink(id); |
| if (uiDeviceLink != null) { |
| boolean remaining = uiDeviceLink.detachBackingLink(link); |
| if (remaining) { |
| postEvent(LINK_ADDED_OR_UPDATED, uiDeviceLink, MEMO_UPDATED); |
| } else { |
| uiTopology.remove(uiDeviceLink); |
| postEvent(LINK_REMOVED, uiDeviceLink, MEMO_REMOVED); |
| } |
| } else { |
| log.warn(E_NO_ELEMENT, "Device link", id); |
| } |
| } |
| |
| Set<UiDeviceLink> getAllDeviceLinks() { |
| return uiTopology.allDeviceLinks(); |
| } |
| |
| // === HOSTS |
| |
| private EdgeLink synthesizeLink(Host h) { |
| return createEdgeLink(h, true); |
| } |
| |
| private UiHost addNewHost(Host h) { |
| UiHost host = new UiHost(uiTopology, h); |
| uiTopology.add(host); |
| |
| EdgeLink elink = synthesizeLink(h); |
| UiLinkId elinkId = uiLinkId(elink); |
| host.setEdgeLinkId(elinkId); |
| |
| // add synthesized edge link to the topology |
| addNewEdgeLink(elinkId); |
| |
| return host; |
| } |
| |
| private void insertNewUiEdgeLink(UiLinkId id) { |
| addNewEdgeLink(id); |
| } |
| |
| private void updateHost(UiHost uiHost, Host h) { |
| UiEdgeLink existing = uiTopology.findEdgeLink(uiHost.edgeLinkId()); |
| |
| // TODO: review - do we need EdgeLink now that we are creating from id only? |
| EdgeLink currentElink = synthesizeLink(h); |
| UiLinkId currentElinkId = uiLinkId(currentElink); |
| |
| if (existing != null) { |
| if (!currentElinkId.equals(existing.id())) { |
| // edge link has changed |
| insertNewUiEdgeLink(currentElinkId); |
| uiHost.setEdgeLinkId(currentElinkId); |
| |
| uiTopology.remove(existing); |
| } |
| |
| } else { |
| // no previously existing edge link |
| insertNewUiEdgeLink(currentElinkId); |
| uiHost.setEdgeLinkId(currentElinkId); |
| |
| } |
| |
| HostLocation hloc = h.location(); |
| uiHost.setLocation(hloc.deviceId(), hloc.port()); |
| } |
| |
| private void loadHosts() { |
| for (Host h : services.host().getHosts()) { |
| UiHost host = addNewHost(h); |
| updateHost(host, h); |
| } |
| } |
| |
| // invoked from UiSharedTopologyModel host listener |
| void addOrUpdateHost(Host host) { |
| HostId id = host.id(); |
| String memo = MEMO_UPDATED; |
| UiHost uiHost = uiTopology.findHost(id); |
| if (uiHost == null) { |
| uiHost = addNewHost(host); |
| memo = MEMO_ADDED; |
| } |
| updateHost(uiHost, host); |
| |
| postEvent(HOST_ADDED_OR_UPDATED, uiHost, memo); |
| } |
| |
| // invoked from UiSharedTopologyModel host listener |
| void moveHost(Host host, Host prevHost) { |
| UiHost uiHost = uiTopology.findHost(prevHost.id()); |
| if (uiHost != null) { |
| updateHost(uiHost, host); |
| postEvent(HOST_MOVED, uiHost, MEMO_MOVED); |
| } else { |
| log.warn(E_NO_ELEMENT, "host", prevHost.id()); |
| } |
| } |
| |
| // package private for unit test access |
| UiHost accessHost(HostId id) { |
| return uiTopology.findHost(id); |
| } |
| |
| // invoked from UiSharedTopologyModel host listener |
| void removeHost(Host host) { |
| HostId id = host.id(); |
| UiHost uiHost = uiTopology.findHost(id); |
| if (uiHost != null) { |
| UiEdgeLink edgeLink = uiTopology.findEdgeLink(uiHost.edgeLinkId()); |
| uiTopology.remove(edgeLink); |
| uiTopology.remove(uiHost); |
| postEvent(HOST_REMOVED, uiHost, MEMO_REMOVED); |
| } else { |
| log.warn(E_NO_ELEMENT, "host", id); |
| } |
| } |
| |
| Set<UiHost> getAllHosts() { |
| return uiTopology.allHosts(); |
| } |
| |
| |
| // === SYNTHETIC LINKS |
| |
| List<UiSynthLink> getSynthLinks(RegionId regionId) { |
| return uiTopology.findSynthLinks(regionId); |
| } |
| |
| Map<UiLinkId, UiSynthLink> relevantSynthLinks(RegionId regionId) { |
| Map<UiLinkId, UiSynthLink> result = new HashMap<>(); |
| for (UiSynthLink sl : getSynthLinks(regionId)) { |
| result.put(sl.original().id(), sl); |
| } |
| return result; |
| } |
| |
| /** |
| * Refreshes the internal state. |
| */ |
| public void refresh() { |
| // fix up internal linkages to ensure they are correct |
| |
| // make sure regions reflect layout containment hierarchy |
| fixupContainmentHierarchy(uiTopology.nullRegion()); |
| uiTopology.allRegions().forEach(this::fixupContainmentHierarchy); |
| |
| // make sure devices and hosts are in the correct region |
| Set<UiDevice> allDevices = uiTopology.allDevices(); |
| Set<UiHost> allHosts = uiTopology.allHosts(); |
| |
| services.region().getRegions().forEach(r -> { |
| RegionId rid = r.id(); |
| UiRegion region = uiTopology.findRegion(rid); |
| if (region != null) { |
| reconcileDevicesAndHostsWithRegion(allDevices, allHosts, rid, region); |
| } else { |
| log.warn("No UiRegion in topology for ID {}", rid); |
| } |
| }); |
| |
| // what is left over, must belong to the null-region |
| Set<DeviceId> leftOver = new HashSet<>(allDevices.size()); |
| allDevices.forEach(d -> leftOver.add(d.id())); |
| uiTopology.nullRegion().reconcileDevices(leftOver); |
| |
| Set<HostId> leftOverHosts = new HashSet<>(allHosts.size()); |
| allHosts.forEach(h -> leftOverHosts.add(h.id())); |
| uiTopology.nullRegion().reconcileHosts(leftOverHosts); |
| |
| // now that we have correct region hierarchy, and devices are in their |
| // respective regions, we can compute synthetic links for each region. |
| uiTopology.computeSynthLinks(); |
| } |
| |
| private void reconcileDevicesAndHostsWithRegion(Set<UiDevice> allDevices, |
| Set<UiHost> allHosts, |
| RegionId rid, |
| UiRegion region) { |
| Set<DeviceId> deviceIds = services.region().getRegionDevices(rid); |
| Set<HostId> hostIds = new HashSet<>(); |
| region.reconcileDevices(deviceIds); |
| |
| deviceIds.forEach(devId -> { |
| UiDevice dev = uiTopology.findDevice(devId); |
| if (dev != null) { |
| dev.setRegionId(rid); |
| allDevices.remove(dev); |
| } else { |
| log.warn("Region device ID {} but no UiDevice in topology", |
| devId); |
| } |
| |
| Set<Host> hosts = services.host().getConnectedHosts(devId); |
| for (Host h : hosts) { |
| HostId hid = h.id(); |
| hostIds.add(hid); |
| UiHost host = uiTopology.findHost(hid); |
| |
| if (host != null) { |
| host.setRegionId(rid); |
| allHosts.remove(host); |
| } else { |
| log.warn("Region host ID {} but no UiHost in topology", |
| hid); |
| } |
| } |
| }); |
| |
| region.reconcileHosts(hostIds); |
| } |
| |
| |
| // === CACHE STATISTICS |
| |
| /** |
| * Returns a detailed (multi-line) string showing the contents of the cache. |
| * |
| * @return detailed string |
| */ |
| public String dumpString() { |
| return uiTopology.dumpString(); |
| } |
| |
| /** |
| * Returns the number of members in the cluster. |
| * |
| * @return number of cluster members |
| */ |
| public int clusterMemberCount() { |
| return uiTopology.clusterMemberCount(); |
| } |
| |
| /** |
| * Returns the number of regions in the topology. |
| * |
| * @return number of regions |
| */ |
| public int regionCount() { |
| return uiTopology.regionCount(); |
| } |
| |
| /** |
| * Returns the number of devices in the topology. |
| * |
| * @return number of devices |
| */ |
| public int deviceCount() { |
| return uiTopology.deviceCount(); |
| } |
| |
| /** |
| * Returns the number of device links in the topology. |
| * |
| * @return number of device links |
| */ |
| public int deviceLinkCount() { |
| return uiTopology.deviceLinkCount(); |
| } |
| |
| /** |
| * Returns the number of edge links in the topology. |
| * |
| * @return number of edge links |
| */ |
| public int edgeLinkCount() { |
| return uiTopology.edgeLinkCount(); |
| } |
| |
| /** |
| * Returns the number of hosts in the topology. |
| * |
| * @return number of hosts |
| */ |
| public int hostCount() { |
| return uiTopology.hostCount(); |
| } |
| |
| /** |
| * Returns the number of synthetic links in the topology. |
| * |
| * @return the number of synthetic links |
| */ |
| public int synthLinkCount() { |
| return uiTopology.synthLinkCount(); |
| } |
| } |